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 delete
ing it (with the default_delete
r). 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 templateunique_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
What Is a "Cache-Friendly" Code
Virtual/Pure Virtual Explained
Why Can't C++ Be Parsed With a Lr(1) Parser
Why Doesn't Polymorphism Work Without Pointers/References
Why Use Static_Cast≪Int≫(X) Instead of (Int)X
What Happens If I Define a 0-Size Array in C/C++
Use of 'Const' For Function Parameters
Why Should the Implementation and the Declaration of a Template Class Be in the Same Header File
Difference Between the Dot (.) Operator and -≫ in C++
Function With Same Name But Different Signature in Derived Class
Why Are Preprocessor Macros Evil and What Are the Alternatives
Raii and Smart Pointers in C++
Convert a String in C++ to Upper Case
C++ Reading CSV File and Assigning Values to Array
If You Shouldn't Throw Exceptions in a Destructor, How to Handle Errors in It