int* p;
and
int *p;
is not about right and wrong, but
about style and emphasis.
int *p;
and explains it "*p is what is the int". They may
point to the C (and C++) declaration grammar to argue
for the correctness of the style. Indeed, the * binds
to the name p
in the grammar.
int* p;
and explains it "p is a
pointer to an int". Indeed the type of p is
int*
.
The recommended style is the second one, but stick to one pointer per declaration and always initialize variables and the source of confusion disappears.
int i; double d[ARRAYNUM]; int *p1 = &i; // or int *p1; p1 = i; double *p2 = d; // or double *p2 = &(d[0]); // or double *p2; p2 = d;
#define NULL 0
), to ensure portability never
assume this. The safety way is assigning pointer to
NULL
(always do this when declare a pointer
but not initialize it yet)
pointertype *pointer = NULL;and using
if (pointer == NULL)
or
if (pointer != NULL)
comparison instead of
if (!pointer)
. NULL
is not
undefined, as
undefined is like this
char *p1 = new char[CHARNUM + 1]; char *p2 = p1; delete []p1; // memory is deallocated cout << p2[0] << endl; // memory is accessed, but undefined!
This is called dangling pointer, a pointer that points to an object that is already deleted.
Every pointer has an associated type. The difference between pointers of different data types is neither in the representation of the pointer nor in the values (addresses) the pointers may hold—these are generally the same for all data pointers. The difference, rather, is in the type of the object being addressed. The type of a pointer instructs the compiler how to interpret the memory found at a particular address as well as how much memory that interpretation should span. However, function pointers, which address the program text segment, are different from data pointers.
Pointer cannot be initialized or assigned the address value of
an object of another type. It is disallowed because although
all pointers can hold any memory address value, may interpret
the layout and extent of that memory very differently (a
function pointer cannot be assigned to it). void
pointer can be assigned the address value of any data pointer
type to transport that address value or compare it to another
address value, but cannot operate on the object pointed by it.
Smart pointers are objects that look and feel like pointers, but are smarter. To look and feel like pointers, smart pointer need to have the same interface that pointers do: they need to support pointer operations like dereferencing (operator*) and indirection (operator->). An object that looks and feels like something else is called a proxy object, or just proxy.
Smart pointer take care of dangling pointers, memory leaks, allocation failures and other memory management. The simplest example isauto_ptr
, which is included
in the header <memory>
. It is a simple
wrapper around a regular pointer, the smartness is the
destructor takes care of deleting the pointer.
template <typename T> class auto_ptr { T *ptr; public: explicit auto_ptr(T *p = NULL) : ptr(p) {} ~auto_ptr() { delete ptr; } T& operator*() { return *ptr; } T* operator->() { return ptr; } // ... }; template <typename T> auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs) { if (this != &rhs) { delete ptr; ptr = rhs.ptr; rhs.ptr = NULL; } return *this; } // usage in application, need include <memory> auto_ptr<ClassName> p(new ClassName); p->DoSomething(); // no need to delete p anymoreReason to use it:
auto_ptr
to NULL
explicitly), dangling pointer (auto_ptr
solves
this by setting its pointer to NULL when it is copied).
q = p
, where p
and
q
are smart pointers.
p
and have q
point to this copy.
p
and
q
point to the same object, but transfer the
responsibility for cleaning up ("ownership") from
p
to q
. Generally
auto_ptr
fall into this class: the assigned
to holds the pointer and the assigned holds NULL.
q = p
causes the count of the object
pointed by p
to increase by one.
DoSomething()
throws
an exception, all the lines after it will not get executed
and p
will never get deleted! If lucky this
leads only to memory leaks, but if ClassName
may free some other resources in its destructor (file
handles, threads, transactions, COM references, mutexes) and
so not calling it may cause severe resource locks. Using a
smart pointer, however, p
will be cleaned up
whenever it gets out of scope, whether it was during the
normal path of execution or during the stack unwinding
caused by throwing an exception.
To write exception safe code with regular pointers
ClassName *p; try { p = new ClassName; p->DoSomething(); delete p; } catch (...) { delete p; throw; }
string
class is commonly implemented using COW
semantics.
string s1("Hello"); string s2 = s1; // s2 and s1 point to the same buffer of characters s2 += " World!"; // allocate new buffer for s2 before appending, so s1 is unchanged
Optimized allocation schemes are possible when you can make
some assumptions about the objects to be allocated or the
operating environment. For example, if know that all the
objects will have the same size or that they will all live in a
single thread. Although it is possible to implement optimized
allocation schemes using class-specific new
and
delete
operators, smart pointers give the freedom
to choose whether to use the optimized scheme for each object,
instead of having the scheme set for all objects of a class. It
is therefore possible to match the allocation scheme to
different operating environments and applications without
modifying the code for the entire class.
auto_ptr
is useful as resource handle to minimize
the chance of memory leak when need to return an object
allocated on the free store from a function.
#includ<memory> // optional for Linux GCC #include<iostream> using namespace std; struct S { S() { cout << "make an S\n"; } ~S() { cout << "destroy an S\n"; } S(const S&) { cout << "copy initialize an S\n"; } S& operator=(const S&) { cout << "copy assign an S\n"; } }; S* f() { return new S; // who is responsible for deleting this S? }; auto_ptr<S> g() { return auto_ptr<S>(new S); // explicitly transfer responsibility for deleting this S } int main() { cout << "start main\n"; S* p = f(); cout << "after f() before g()\n"; // S* q = g(); // this error would be caught by the compiler auto_ptr<S> q = g(); cout << "exit main\n"; // leaks *p // implicitly deletes *q }The "move semantics" differs from the usual "copy semantics", so never use
auto_ptr
as a member of a STL
container. The STL containers require the usual copy semantics
during assignment. STL containers may copy and delete their
elements behind the scenes (for example, when they resize
themselves). Therefore, all copies of an element must be
equivalent, or the wrong copy may be the one to survive all
this copying and deleting. This means that some smart pointers
cannot be used within STL containers, specifically the standard
auto_ptr
and any ownership-transferring pointer.
std::vector<auto_ptr<X> >v; // errorBut if the smart pointer assignment is not ownership-transfer, it can be used and release the burden to manually delete the pointers in STL container.
vector<linked_ptr<Base> > v; v.push_back(new Base); v.push_back(new Derived); // cleanup is automatic
auto_ptr
holds a pointer to an indiviual element,
not a pointer to an array. This is an error because the
destructor will delete the pointer using delete
rather than delete []
and will fail to invoke
the destructor for the last n-1 elements. There is no
"auto_array", just use the vector
.
auto_ptr<X> p(new X[n]); // error vector<X> v(n); // ok
Reference counting/linking smart pointer can leak in the case of circular references (i.e., when the pointed object itself contains a counted pointer, which points to an object that contains the original counted pointer). Its advantage over other schemes is that it is both simple to implement and deterministic. The deterministic behavior may be important in some real time systems, where cannot allow the system to suddenly wait while the garbage collector performs its housekeeping duties. They require using locks if the pointers are used by more than one thread of execution.
Suitability of different smart pointers for various situations:auto_ptr