Which Kind of Pointer Do I Use When

Which kind of pointer do I use when?

Shared ownership:

The shared_ptr and weak_ptr the standard adopted are pretty much the same as their Boost counterparts. Use them when you need to share a resource and don't know which one will be the last to be alive. Use weak_ptr to observe the shared resource without influencing its lifetime, not to break cycles. Cycles with shared_ptr shouldn't normally happen - two resources can't own each other.

Note that Boost additionally offers shared_array, which might be a suitable alternative to shared_ptr<std::vector<T> const>.

Next, Boost offers intrusive_ptr, which are a lightweight solution if your resource offers reference-counted management already and you want to adopt it to the RAII principle. This one was not adopted by the standard.

Unique ownership:

Boost also has a scoped_ptr, which is not copyable and for which you can not specify a deleter. std::unique_ptr is boost::scoped_ptr on steroids and should be your default choice when you need a smart pointer. It allows you to specify a deleter in its template arguments and is movable, unlike boost::scoped_ptr. It is also fully usable in STL containers as long as you don't use operations that need copyable types (obviously).

Note again, that Boost has an array version: scoped_array, which the standard unified by requiring std::unique_ptr<T[]> partial specialization that will delete[] the pointer instead of deleteing it (with the default_deleter). std::unique_ptr<T[]> also offers operator[] instead of operator* and operator->.

Note that std::auto_ptr is still in the standard, but it is deprecated.
§D.10 [depr.auto.ptr]

The class template auto_ptr is deprecated. [ Note: The class template unique_ptr (20.7.1) provides a better solution. —end note ]

No ownership:

Use dumb pointers (raw pointers) or references for non-owning references to resources and when you know that the resource will outlive the referencing object / scope. Prefer references and use raw pointers when you need either nullability or resettability.

If you want a non-owning reference to a resource, but you don't know if the resource will outlive the object that references it, pack the resource in a shared_ptr and use a weak_ptr - you can test if the parent shared_ptr is alive with lock, which will return a shared_ptr that is non-null if the resource still exists. If want to test whether the resource is dead, use expired. The two may sound similar, but are very different in the face of concurrent execution, as expired only guarantees its return value for that single statement. A seemingly innocent test like

if(!wptr.expired())
something_assuming_the_resource_is_still_alive();

is a potential race condition.

Which type should I use for a pointer ? ptrdiff_t or void*?

Pointers in C are of type T* where T is the type pointed to; void* is the generic pointer type. Usually, you let C implicitly convert void* to something useful, e.g.

char *buffer = malloc(1024);

ptrdiff_t is the type returned by the subtraction of two pointers, e.g.

ptrdiff_t d = write_ptr - buffer;
// now you know the write_ptr is d bytes beyond the start of the buffer

ptrdiff_t is an integral type, not a pointer type; you cannot use the indirection operator * on it. (Nor can you meaningfully use it on a void*, by the way.)

Had you wanted to store a pointer in an integer type, uintptr_t would have been appropriate.

Which kind of (auto) pointer to use?

Personally I think a raw pointer (or reference) is okay here. Smart pointers are concerned with managing the lifetime of the object pointed to, and in this case MyMapper isn't managing the lifetime of that object, MyClass is. You also shouldn't have a smart pointer pointing to an object that was not dynamically allocated (which the hash map isn't in this case).

Personally, I'd use something like the following:

class MyMapper
{
public:
MyMapper(HashMap<string, Id> &map)
: _map(map)
{
}
private:
HashMap<string, Id> &_map
};

Note that this will prevent MyMapper from having an assignment operator, and it can only work if it's acceptable to pass the HashMap in the constructor; if that is a problem, I'd make the member a pointer (though I'd still pass the argument as a reference, and do _map(&map) in the initializer list).

If it's possible for MyMapper or any other class using the hash map to outlive MyClass, then you'd have to start thinking about smart pointers. In that case, I would probably recommend std::shared_ptr, but you'd have to use it everywhere: _hugeIdMap would have to be a shared_ptr to a dynamically allocated value, not a regular non-pointer field.


Update:

Since you said that using a reference is not acceptable due to the project's coding standards, I would suggest just sticking with a raw pointer for the reasons mentioned above.

Declaring type of pointers?

Type-safety. If you don't know what p is supposed to point to, then there'd be nothing to prevent category errors like

*p = "Nonsense";
int i = *p;

Static type checking is a very powerful tool for preventing all kinds of errors like that.

C and C++ also support pointer arithmetic, which only works if the size of the target type is known.

address occupies same amount of memory whatever my be the type

That's true for today's popular platforms. But there have been platforms for which that wasn't the case. For example, a pointer to a multi-byte word could be smaller than a pointer to a single byte, since it doesn't need to represent the byte's offset within the word.

Pointers in C: when to use the ampersand and the asterisk?

You have pointers and values:

int* p; // variable p is pointer to integer type
int i; // integer value

You turn a pointer into a value with *:

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to

You turn a value into a pointer with &:

int* p2 = &i; // pointer p2 will point to the address of integer i

Edit:
In the case of arrays, they are treated very much like pointers. If you think of them as pointers, you'll be using * to get at the values inside of them as explained above, but there is also another, more common way using the [] operator:

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

To get the second element:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

So the [] indexing operator is a special form of the * operator, and it works like this:

a[i] == *(a + i);  // these two statements are the same thing

When should I use raw pointers over smart pointers?

No, it's not true. If a function needs a pointer and has nothing to do with ownership, then I strongly believe that a regular pointer should be passed for the following reasons:

  • No ownership, therefore you don't know what kind of a smart pointer to pass
  • If you pass a specific pointer, like shared_ptr, then you won't be able to pass, say, scoped_ptr

The rule would be this - if you know that an entity must take a certain kind of ownership of the object, always use smart pointers - the one that gives you the kind of ownership you need. If there is no notion of ownership, never use smart pointers.

Example1:

void PrintObject(shared_ptr<const Object> po) //bad
{
if(po)
po->Print();
else
log_error();
}

void PrintObject(const Object* po) //good
{
if(po)
po->Print();
else
log_error();
}

Example2:

Object* createObject() //bad
{
return new Object;
}

some_smart_ptr<Object> createObject() //good
{
return some_smart_ptr<Object>(new Object);
}

Which data type to be used for a generic function pointer?

For a generic function pointer, use any function pointer type.

C 2018 6.3.2.3 8 says:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer…

This means that you can convert any function pointer to a different type of function pointer. As long as you convert it back to the original type when calling the function, the behavior is defined, and it will correctly call the original function.

C does not provide a designated generic function pointer type, like void * for objects. You just pick one for your program, like void (*)(), and use that.



Related Topics



Leave a reply



Submit