C++ Primer 读书笔记之动态内存
2014-11-19 16:15
253 查看
动态内存
1 Globalobjects are allocated at program start-up and destroyed when the program ends.Local, automatic objects are createdand destroyed when the block in which they are defined is entered and exited.Local static
objects are allocatedbefore their first use and are destroyed when the program ends.
2 Staticmemory is used for local static objects, for class static data members ,andfor variables defined outside any function.Stack memory
is used for non static objects defined insidefunctions. Objects allocated in static or stack memory are automaticallycreated and destroyed by the compiler. Stack objects exist only while the blockin which they are defined is executing; static objects
are allocated beforethey are used, and they are destroyed when the program ends.
3 In addition to static or stack memory,every program also has a pool of memory that it can use. This memory isreferred to as the free store orheap.Programs use the heap for objects that they
dynamically allocate—that is, for objects that the program allocatesat run time. The program controls the lifetime of dynamic objects; our codemust explicitly destroy such objects when they are no longer needed.
4 The new library defines two kinds ofsmart pointers that differ in how they manage their underlying pointers:shared_ptr, which allows multiplepointers to refer to the same object, andunique_ptr,which “owns” the object to
which it points. The library also defines acompanion class namedweak_ptr thatis a weak reference to an object managed by a shared_ptr. All three are definedin thememory header.
5
6 make_shared函数
// shared_ptr that points to an int withvalue 42
shared_ptr<int> p3 =make_shared<int>(42);
// p4 points to a string with value9999999999
shared_ptr<string> p4 =make_shared<string>(10, '9');
// p5 points to an int that is valueinitialized (§ 3.3.1 (p. 98)) to 0
shared_ptr<int> p5 =make_shared<int>();
// p6 points to a dynamically allocated,empty vector<string>
auto p6 =make_shared<vector<string>>();
Like the sequential-container emplacemembers , make_shared uses its arguments to construct an object of the giventype. If we do not pass any arguments, then the object is value initialized.
7 Copying and Assigning shared_ptrs
auto p = make_shared<int>(42); //object to which p points has one user
auto q(p); // p and q point to the sameobject
// object to which p and q point has two users
auto r = make_shared<int>(42); // intto which r points has one user
r = q; // assign to r, making it point to a different address
// increase the use count for the object to which q points
// reduce the use count of the object to which r had pointed
// the object r had pointedto has no users; that object is automatically freed
8
Note:If you put shared_ptrs in a container, and you subsequently need touse some, but not all, of the elements, remember to erase the elements you nolonger need.
Programs tend to use dynamic memory for oneof three purposes:
1. They don’t know how many objects they’llneed
2. They don’t know the precise type of theobjects they need
3. They want to share data between severalobjects
The container classes are an example ofclasses that use dynamic memory for the first purpose.
vector<string> v1; // empty vector
{ // new scope
vector<string> v2 = {"a","an", "the"};
v1 =v2; // copies the elements from v2 into v1
} // v2 is destroyed, which destroys theelements in v2
//v1 has three elements, which are copies of the ones originally in v2
Blob<string> b1; // empty Blob
{ // new scope
Blob<string> b2 = {"a", "an","the"};
b1= b2; // b1 and b2 share the same elements
} // b2 is destroyed, but the elements inb2 must not be destroyed
//b1 points to the elements originally created in b2
Note: One common reason to use dynamicmemory is to allow multiple objects to share the same state.
9 //StrBlob.h
#ifndef _STRBLOB_H_
#define_STRBLOB_H_
#include<vector>
#include<memory>
classStrBlob
{
public:
typedefstd::vector<std::string>::size_typesize_type;
StrBlob();
StrBlob(std::initializer_list<std::string>
il);
size_typesize()const{returndata->size();}
boolempty()const{returndata->empty();}
voidpush_back(conststd::string&t){data->push_back(t);}
voidpop_back();
conststd::string&front()const;
conststd::string&back()const;
std::string&front();
std::string&back();
private:
std::shared_ptr<std::vector<std::string>>data;
voidcheck(size_type
i,conststd::string&msg)const;
};
#endif//_STRBLOB_H_
//StrBlob.cpp
#include"StrBlob.h"
StrBlob::StrBlob():data(std::make_shared<std::vector<std::string>>())
{
}
StrBlob::StrBlob(std::initializer_list<std::string>il):data(std::make_shared<std::vector<std::string>>(il))
{
}
voidStrBlob::check(size_typei,conststd::string&msg)const
{
if(i>=data->size())
{
throwstd::out_of_range(msg);
}
}
voidStrBlob::pop_back()
{
check(0,"pop_back
on empty StrBlob");
data->pop_back();
}
std::string&StrBlob::front()
{
check(0,"front
on empty StrBlob");
returndata->front();
}
std::string&StrBlob::back()
{
check(0,"back
on empty StrBlob");
returndata->back();
}
conststd::string&StrBlob::front()const
{
check(0,"front
on empty StrBlob");
returndata->front();
}
conststd::string&StrBlob::back()const
{
check(0,"front
on empty StrBlob");
returndata->back();
}
10 Managing Memory Directly
int *pi = new int; // pi points to a dynamically allocated,unnamed, uninitialized int
By default, dynamically allocated objectsare default initialized, which meansthat objects of built-in or compound type have undefined value; objects ofclass type are initialized by their default constructor:
string *ps = new string; // initialized to empty string
int *pi = new int; // pi points to an uninitialized int
We can initialize a dynamically allocatedobject using direct initialization
int *pi = new int(1024); // object to whichpi points has value 1024
string *ps = new string(10, '9'); // *ps is "9999999999"
// vector with ten elements with valuesfrom 0 to 9
vector<int> *pv = newvector<int>{0,1,2,3,4,5,6,7,8,9};
We can also value initialize a dynamically allocated object by following thetype name with a pair of empty parentheses:
string *ps1 = new string; // default initialized to the empty string
string *ps = new string(); // valueinitialized to the empty string
int *pi1 = new int; // default initialized; *pi1 is undefined
int *pi2 = new int(); // value initialized to 0; *pi2 is 0
BestPractices:For the same reasons as we usuallyinitialize variables, it is also a good idea
to initialize dynamically allocatedobjects.
auto p1 = new auto(obj); // p points to an object of the type of obj
// that object is initializedfrom obj
auto p2 = new auto{a,b,c}; // error: mustuse parentheses for the initializer
PointerValues and delete
int i, *pi1 = &i, *pi2 = nullptr;
double *pd = new double(33), *pd2 =pd;
delete i; // error: i is not a pointer
delete pi1; // undefined: pi1 refers to alocal
delete pd; // ok
delete pd2; // undefined: the memorypointed to by pd2 was already freed
delete pi2; // ok: it is always ok todelete a null pointer
Warning: Dynamic memory managed through built-in pointers (rather than smart
pointers) exists until it is explicitlyfreed.
There are three common problems with usingnew and delete to manage dynamic memory:
1 Forgetting to delete memory.
2 Using an object after it has beendeleted.
3 Deleting the same memory twice.
BestPractices:You can avoid all of these problems byusing smart pointers exclusively.The smart pointer will take care of deletingthe memory only when there are no remaining smart pointers pointing to thatmemory.
11 Usingshared_ptrs with new
shared_ptr<double> p1; // shared_ptrthat can point at a double
shared_ptr<int> p2(new int(42)); //p2 points to an int with value 42
The smart pointer constructors that takepointers are explicit .Hence, we cannot implicitly convert a built-in pointerto a smart pointer; we must use the direct form of initialization to initializea smart pointer:
shared_ptr<int> p1 = newint(1024); // error: must use directinitialization
shared_ptr<int> p2(newint(1024)); // ok: uses directinitialization
shared_ptr<int> clone(int p) {
returnnew int(p); // error: implicit conversion to shared_ptr<int>
}
shared_ptr<int> clone(int p) {
//ok: explicitly create a shared_ptr<int> from int*
return shared_ptr<int>(new int(p));
}
By default, a pointer used to initialize asmart pointer must point to dynamic memory because, by default, smart pointersuse delete to free the associated object. We can bind smart pointers topointers to other kinds of resources. However, to do so, we must
supply our ownoperation to use in place of delete.
// ptr is created and initialized whenprocess is called
void process(shared_ptr<int> ptr)
{
//use ptr
} // ptr goes out of scope and is destroyed
shared_ptr<int> p(new int(42)); //reference count is 1
process(p); // copying p increments itscount; in process the reference count is 2
int i = *p; // ok: reference count is 1
int *x(new int(1024)); // dangerous: x is aplain pointer, not a smart pointer
process(x); // error: cannot convert int* to shared_ptr<int>
process(shared_ptr<int>(x)); //legal, but the memory will be deleted!
int j = *x; // undefined: x is a dangling pointer!
Warning:It is dangerous to use a built-in pointer to access an object ownedby a smart pointer, because we may not know when that object is destroyed.
shared_ptr<int> p(new int(42)); //reference count is 1
int *q = p.get(); // ok: but don't use q in any way that mightdelete its
pointer
{ // new block
// undefined: two independent shared_ptrspoint to the same memory
shared_ptr<int>(q);
} // block ends, q is destroyed, and the memoryto which q points is freed
int foo = *p; // undefined; the memory towhich p points was freed
Warning: Use get only to pass access to the pointer to code that you knowwill not delete the pointer. In particular, never use get to initialize orassign to another smart pointer.
Like assignment, reset updates thereference counts and, if appropriate, deletes the object to which p points. Thereset member is often used together with unique to control changes to theobject shared among several shared_ptrs. Before changing the underlying
object,we check whether we’re the only user. If not, we make a new copy before makingthe change:
if (!p.unique())
p.reset(new string(*p)); // we aren't alone;allocate a new copy
*p += newVal; // now that we know we're theonly pointer, okay to change this object
12 SmartPointers and Exceptions
void f()
{
shared_ptr<int> sp(new int(42)); //allocate a new object
//code that throws an exception that is not caught inside f
} // shared_ptr freed automatically whenthe function ends
void f()
{
int*ip = new int(42); // dynamicallyallocate a new object
//code that throws an exception that is not caught inside f
delete ip; // free the memory before exiting
}
If an exception happens between the new andthe delete, and is not caught inside f, then this memory can never be freed.There is no pointer to this memory outside the function f. Thus, there is noway to free this memory.
//Smart Pointers and Dumb Classes
structdestination
{
};
structconnection
{
};
connectionconnect(destination*)
{
connectionconn;
returnconn;
}
voiddisconnect(connection)
{
cout<<"end_connection"<<std::endl;
}
voidend_connection(connection*p)
{
disconnect(*p);
}
//lambda形式
voidf(destination&d)
{
connectionc=connect(&d);
shared_ptr<connection>p(&c,[](connection*p){
disconnect(*p);
});
}
//voidf(destination &d)
//{
// connection c = connect(&d);
// shared_ptr<connection> p(&c,end_connection);
//}
When p is destroyed, it won’t executedelete on its stored pointer. Instead, p will call end_connection on thatpointer. In turn, end_connection will call disconnect, thus ensuring that theconnection is closed. If f exits normally, then p will be destroyed
as part ofthe return. Moreover, p will also be destroyed, and the connection will beclosed, if an exception occurs.
Caution:Smart Pointer Pitfalls
Smart pointers can provide safety andconvenience for handling dynamically allocated memory only when they are usedproperly. To use smart pointers correctly, we must adhere to a set ofconventions:
• Don’t use the same built-in pointer value to initialize (or reset)more than
one smart pointer.
• Don’t delete the pointer returned from get() .
• Don’t use get() toinitialize or reset another smart pointer.
• If you use a pointer returned by get() , remember that the pointerwill become invalid when the last corresponding smart pointer goes away.
• If you use a smart pointer to manage a resource other than memoryallocated by new, remember to pass a delete.
13unique_ptr
unique_ptr<double> p1; // unique_ptr that can point at a double
unique_ptr<int> p2(new int(42)); //p2 points to int with value 42
Because a unique_ptr owns the object towhich it points, unique_ptr does not
support ordinary copy or assignment:
unique_ptr<string> p1(newstring("Stegosaurus"));
unique_ptr<string> p2(p1); // error: no copy for unique_ptr
unique_ptr<string> p3;
p3 = p2; // error: no assign for unique_ptr
// transfers ownership from p1 (whichpoints to the string Stegosaurus) to p2
unique_ptr<string> p2(p1.release());// release makes p1 null
unique_ptr<string> p3(newstring("Trex"));
// transfers ownership from p3 to p2
p2.reset(p3.release()); // reset deletesthe memory to which p2 had pointed
if we do not use another smart pointer tohold the pointer returned from release, our program takes over responsibilityfor freeing that resource:
p2.release(); // WRONG: p2 won't free the memory and we'velost the pointer
auto p = p2.release(); // ok, but we mustremember to delete(p)
There is one exception to the rule that wecannot copy a unique_ptr: We can copy orassign a unique_ptr that is about to be destroyed. The most common example iswhen we return a unique_ptr from a function:
unique_ptr<int> clone(int p) {
//ok: explicitly create a unique_ptr<int> from int*
return unique_ptr<int>(new int(p));
}
unique_ptr<int> clone(int p) {
unique_ptr<int> ret(new int (p));
// .. .
return ret;
}
// p points to an object of type objT anduses an object of type delT to free that object
// it will call an object named fcn of typedelT
unique_ptr<objT, delT> p (new objT,fcn);
void f(destination &d /* other neededparameters */)
{
connection c = connect(&d); // open the connection
//when p is destroyed, the connection will be closed
unique_ptr<connection,decltype(end_connection)*>
p(&c, end_connection);
//use the connection
//when f exits, even if by an exception, the connection will be properly closed
}
14weak_ptr
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); // wp weakly shares with p; use count in p isunchanged
if (shared_ptr<int> np = wp.lock()) {// true if np is not null
//inside the if, np shares its object with p
}
15Dynamic Arrays
1 Best Practices:Mostapplications should use a library container rather than dynamically allocatedarrays. Using a container is easier, less likely to contain memory managementbugs, and is likely to give better performance.
2 typedef int arrT[42]; // arrT names thetype array of 42 ints
int *p = new arrT; // allocates an array of 42 ints; p points tothe first one
delete [] p; // brackets are necessary because weallocated an array
// up points to an array of tenuninitialized ints
unique_ptr<int[]> up(new int[10]);
up.release(); // automatically uses delete[] to destroy itspointer
Unlike unique_ptr, shared_ptrs provide nodirect support for managing a dynamic array. If we want to use a shared_ptr tomanage a dynamic array, we must provide our own deleter:
// to use a shared_ptr we must supply adeleter
shared_ptr<int> sp(new int[10],[](int *p) { delete[] p; });
sp.reset(); // uses the lambda we suppliedthat uses delete[] to free the array
// shared_ptrs don't have subscriptoperator and don't support pointer arithmetic
for (size_t i = 0; i != 10; ++i)
*(sp.get() + i) = i; // use get to get a built-in pointer
16 Theallocator Class
//int n = 3;
//string *const p = new string
;
//string str;
//string *q = p;
//while (cin >> str && q != p + n)
//{
// *q = str;
// ++q;
//}
//const size_t size = q - p;
//delete[]p;
allocator<string>salloc;
autop=salloc.allocate(3);
autoq=p;
stringstr;
while(cin>>str&&q!=p+3)
{
salloc.construct(q,str);
++q;
}
constsize_tsize=q-p;
q=p;
while(q!=p+3)
{
cout<<*q<<"
";
++q;
}
cout<<std::endl;
while(q!=p)
{
salloc.destroy(--q);
}
salloc.deallocate(p,3);
allocator<string> alloc; // object that can allocate strings
auto const p = alloc.allocate(n); //allocate n unconstructed strings
auto q = p; // q will point to one past thelast constructed element
alloc.construct(q++); // *q is the empty string
alloc.construct(q++, 10, 'c'); // *q is cccccccccc
alloc.construct(q++, "hi"); // *q is hi!
Warning: We must construct objects in order to use memory returned by allocate.Using unconstructed memory in other ways is undefined.
while (q != p)
alloc.destroy(--q); // free the strings we actually allocated
Warning:We may destroy only elements that are actually constructed.
alloc.deallocate(p, n);
The pointer we pass to deallocate cannot benull; it must point to memory allocated by allocate. Moreover, the sizeargument passed to deallocate must be the same size as used in the call toallocate that obtained the memory to which the pointer points.
1 Globalobjects are allocated at program start-up and destroyed when the program ends.Local, automatic objects are createdand destroyed when the block in which they are defined is entered and exited.Local static
objects are allocatedbefore their first use and are destroyed when the program ends.
2 Staticmemory is used for local static objects, for class static data members ,andfor variables defined outside any function.Stack memory
is used for non static objects defined insidefunctions. Objects allocated in static or stack memory are automaticallycreated and destroyed by the compiler. Stack objects exist only while the blockin which they are defined is executing; static objects
are allocated beforethey are used, and they are destroyed when the program ends.
3 In addition to static or stack memory,every program also has a pool of memory that it can use. This memory isreferred to as the free store orheap.Programs use the heap for objects that they
dynamically allocate—that is, for objects that the program allocatesat run time. The program controls the lifetime of dynamic objects; our codemust explicitly destroy such objects when they are no longer needed.
4 The new library defines two kinds ofsmart pointers that differ in how they manage their underlying pointers:shared_ptr, which allows multiplepointers to refer to the same object, andunique_ptr,which “owns” the object to
which it points. The library also defines acompanion class namedweak_ptr thatis a weak reference to an object managed by a shared_ptr. All three are definedin thememory header.
5
6 make_shared函数
// shared_ptr that points to an int withvalue 42
shared_ptr<int> p3 =make_shared<int>(42);
// p4 points to a string with value9999999999
shared_ptr<string> p4 =make_shared<string>(10, '9');
// p5 points to an int that is valueinitialized (§ 3.3.1 (p. 98)) to 0
shared_ptr<int> p5 =make_shared<int>();
// p6 points to a dynamically allocated,empty vector<string>
auto p6 =make_shared<vector<string>>();
Like the sequential-container emplacemembers , make_shared uses its arguments to construct an object of the giventype. If we do not pass any arguments, then the object is value initialized.
7 Copying and Assigning shared_ptrs
auto p = make_shared<int>(42); //object to which p points has one user
auto q(p); // p and q point to the sameobject
// object to which p and q point has two users
auto r = make_shared<int>(42); // intto which r points has one user
r = q; // assign to r, making it point to a different address
// increase the use count for the object to which q points
// reduce the use count of the object to which r had pointed
// the object r had pointedto has no users; that object is automatically freed
8
// factory returns a shared_ptr pointing toa dynamically allocated object shared_ptr<Foo> factory(T arg) { // process arg as appropriate // shared_ptr will take care of deleting this memory return make_shared<Foo>(arg); } void use_factory(T arg) { shared_ptr<Foo> p = factory(arg); //use p } // p goes out of scope; the memory towhich p points is automatically freed shared_ptr<Foo> use_factory(T arg) { shared_ptr<Foo> p = factory(arg); //use p return p; // reference count is incremented when we return p } // p goes out of scope; the memory towhich p points is not freed
Note:If you put shared_ptrs in a container, and you subsequently need touse some, but not all, of the elements, remember to erase the elements you nolonger need.
Programs tend to use dynamic memory for oneof three purposes:
1. They don’t know how many objects they’llneed
2. They don’t know the precise type of theobjects they need
3. They want to share data between severalobjects
The container classes are an example ofclasses that use dynamic memory for the first purpose.
vector<string> v1; // empty vector
{ // new scope
vector<string> v2 = {"a","an", "the"};
v1 =v2; // copies the elements from v2 into v1
} // v2 is destroyed, which destroys theelements in v2
//v1 has three elements, which are copies of the ones originally in v2
Blob<string> b1; // empty Blob
{ // new scope
Blob<string> b2 = {"a", "an","the"};
b1= b2; // b1 and b2 share the same elements
} // b2 is destroyed, but the elements inb2 must not be destroyed
//b1 points to the elements originally created in b2
Note: One common reason to use dynamicmemory is to allow multiple objects to share the same state.
9 //StrBlob.h
#ifndef _STRBLOB_H_
#define_STRBLOB_H_
#include<vector>
#include<memory>
classStrBlob
{
public:
typedefstd::vector<std::string>::size_typesize_type;
StrBlob();
StrBlob(std::initializer_list<std::string>
il);
size_typesize()const{returndata->size();}
boolempty()const{returndata->empty();}
voidpush_back(conststd::string&t){data->push_back(t);}
voidpop_back();
conststd::string&front()const;
conststd::string&back()const;
std::string&front();
std::string&back();
private:
std::shared_ptr<std::vector<std::string>>data;
voidcheck(size_type
i,conststd::string&msg)const;
};
#endif//_STRBLOB_H_
//StrBlob.cpp
#include"StrBlob.h"
StrBlob::StrBlob():data(std::make_shared<std::vector<std::string>>())
{
}
StrBlob::StrBlob(std::initializer_list<std::string>il):data(std::make_shared<std::vector<std::string>>(il))
{
}
voidStrBlob::check(size_typei,conststd::string&msg)const
{
if(i>=data->size())
{
throwstd::out_of_range(msg);
}
}
voidStrBlob::pop_back()
{
check(0,"pop_back
on empty StrBlob");
data->pop_back();
}
std::string&StrBlob::front()
{
check(0,"front
on empty StrBlob");
returndata->front();
}
std::string&StrBlob::back()
{
check(0,"back
on empty StrBlob");
returndata->back();
}
conststd::string&StrBlob::front()const
{
check(0,"front
on empty StrBlob");
returndata->front();
}
conststd::string&StrBlob::back()const
{
check(0,"front
on empty StrBlob");
returndata->back();
}
10 Managing Memory Directly
int *pi = new int; // pi points to a dynamically allocated,unnamed, uninitialized int
By default, dynamically allocated objectsare default initialized, which meansthat objects of built-in or compound type have undefined value; objects ofclass type are initialized by their default constructor:
string *ps = new string; // initialized to empty string
int *pi = new int; // pi points to an uninitialized int
We can initialize a dynamically allocatedobject using direct initialization
int *pi = new int(1024); // object to whichpi points has value 1024
string *ps = new string(10, '9'); // *ps is "9999999999"
// vector with ten elements with valuesfrom 0 to 9
vector<int> *pv = newvector<int>{0,1,2,3,4,5,6,7,8,9};
We can also value initialize a dynamically allocated object by following thetype name with a pair of empty parentheses:
string *ps1 = new string; // default initialized to the empty string
string *ps = new string(); // valueinitialized to the empty string
int *pi1 = new int; // default initialized; *pi1 is undefined
int *pi2 = new int(); // value initialized to 0; *pi2 is 0
BestPractices:For the same reasons as we usuallyinitialize variables, it is also a good idea
to initialize dynamically allocatedobjects.
auto p1 = new auto(obj); // p points to an object of the type of obj
// that object is initializedfrom obj
auto p2 = new auto{a,b,c}; // error: mustuse parentheses for the initializer
PointerValues and delete
int i, *pi1 = &i, *pi2 = nullptr;
double *pd = new double(33), *pd2 =pd;
delete i; // error: i is not a pointer
delete pi1; // undefined: pi1 refers to alocal
delete pd; // ok
delete pd2; // undefined: the memorypointed to by pd2 was already freed
delete pi2; // ok: it is always ok todelete a null pointer
Warning: Dynamic memory managed through built-in pointers (rather than smart
pointers) exists until it is explicitlyfreed.
There are three common problems with usingnew and delete to manage dynamic memory:
1 Forgetting to delete memory.
2 Using an object after it has beendeleted.
3 Deleting the same memory twice.
BestPractices:You can avoid all of these problems byusing smart pointers exclusively.The smart pointer will take care of deletingthe memory only when there are no remaining smart pointers pointing to thatmemory.
11 Usingshared_ptrs with new
shared_ptr<double> p1; // shared_ptrthat can point at a double
shared_ptr<int> p2(new int(42)); //p2 points to an int with value 42
The smart pointer constructors that takepointers are explicit .Hence, we cannot implicitly convert a built-in pointerto a smart pointer; we must use the direct form of initialization to initializea smart pointer:
shared_ptr<int> p1 = newint(1024); // error: must use directinitialization
shared_ptr<int> p2(newint(1024)); // ok: uses directinitialization
shared_ptr<int> clone(int p) {
returnnew int(p); // error: implicit conversion to shared_ptr<int>
}
shared_ptr<int> clone(int p) {
//ok: explicitly create a shared_ptr<int> from int*
return shared_ptr<int>(new int(p));
}
By default, a pointer used to initialize asmart pointer must point to dynamic memory because, by default, smart pointersuse delete to free the associated object. We can bind smart pointers topointers to other kinds of resources. However, to do so, we must
supply our ownoperation to use in place of delete.
// ptr is created and initialized whenprocess is called
void process(shared_ptr<int> ptr)
{
//use ptr
} // ptr goes out of scope and is destroyed
shared_ptr<int> p(new int(42)); //reference count is 1
process(p); // copying p increments itscount; in process the reference count is 2
int i = *p; // ok: reference count is 1
int *x(new int(1024)); // dangerous: x is aplain pointer, not a smart pointer
process(x); // error: cannot convert int* to shared_ptr<int>
process(shared_ptr<int>(x)); //legal, but the memory will be deleted!
int j = *x; // undefined: x is a dangling pointer!
Warning:It is dangerous to use a built-in pointer to access an object ownedby a smart pointer, because we may not know when that object is destroyed.
shared_ptr<int> p(new int(42)); //reference count is 1
int *q = p.get(); // ok: but don't use q in any way that mightdelete its
pointer
{ // new block
// undefined: two independent shared_ptrspoint to the same memory
shared_ptr<int>(q);
} // block ends, q is destroyed, and the memoryto which q points is freed
int foo = *p; // undefined; the memory towhich p points was freed
Warning: Use get only to pass access to the pointer to code that you knowwill not delete the pointer. In particular, never use get to initialize orassign to another smart pointer.
Like assignment, reset updates thereference counts and, if appropriate, deletes the object to which p points. Thereset member is often used together with unique to control changes to theobject shared among several shared_ptrs. Before changing the underlying
object,we check whether we’re the only user. If not, we make a new copy before makingthe change:
if (!p.unique())
p.reset(new string(*p)); // we aren't alone;allocate a new copy
*p += newVal; // now that we know we're theonly pointer, okay to change this object
12 SmartPointers and Exceptions
void f()
{
shared_ptr<int> sp(new int(42)); //allocate a new object
//code that throws an exception that is not caught inside f
} // shared_ptr freed automatically whenthe function ends
void f()
{
int*ip = new int(42); // dynamicallyallocate a new object
//code that throws an exception that is not caught inside f
delete ip; // free the memory before exiting
}
If an exception happens between the new andthe delete, and is not caught inside f, then this memory can never be freed.There is no pointer to this memory outside the function f. Thus, there is noway to free this memory.
//Smart Pointers and Dumb Classes
structdestination
{
};
structconnection
{
};
connectionconnect(destination*)
{
connectionconn;
returnconn;
}
voiddisconnect(connection)
{
cout<<"end_connection"<<std::endl;
}
voidend_connection(connection*p)
{
disconnect(*p);
}
//lambda形式
voidf(destination&d)
{
connectionc=connect(&d);
shared_ptr<connection>p(&c,[](connection*p){
disconnect(*p);
});
}
//voidf(destination &d)
//{
// connection c = connect(&d);
// shared_ptr<connection> p(&c,end_connection);
//}
When p is destroyed, it won’t executedelete on its stored pointer. Instead, p will call end_connection on thatpointer. In turn, end_connection will call disconnect, thus ensuring that theconnection is closed. If f exits normally, then p will be destroyed
as part ofthe return. Moreover, p will also be destroyed, and the connection will beclosed, if an exception occurs.
Caution:Smart Pointer Pitfalls
Smart pointers can provide safety andconvenience for handling dynamically allocated memory only when they are usedproperly. To use smart pointers correctly, we must adhere to a set ofconventions:
• Don’t use the same built-in pointer value to initialize (or reset)more than
one smart pointer.
• Don’t delete the pointer returned from get() .
• Don’t use get() toinitialize or reset another smart pointer.
• If you use a pointer returned by get() , remember that the pointerwill become invalid when the last corresponding smart pointer goes away.
• If you use a smart pointer to manage a resource other than memoryallocated by new, remember to pass a delete.
13unique_ptr
unique_ptr<double> p1; // unique_ptr that can point at a double
unique_ptr<int> p2(new int(42)); //p2 points to int with value 42
Because a unique_ptr owns the object towhich it points, unique_ptr does not
support ordinary copy or assignment:
unique_ptr<string> p1(newstring("Stegosaurus"));
unique_ptr<string> p2(p1); // error: no copy for unique_ptr
unique_ptr<string> p3;
p3 = p2; // error: no assign for unique_ptr
// transfers ownership from p1 (whichpoints to the string Stegosaurus) to p2
unique_ptr<string> p2(p1.release());// release makes p1 null
unique_ptr<string> p3(newstring("Trex"));
// transfers ownership from p3 to p2
p2.reset(p3.release()); // reset deletesthe memory to which p2 had pointed
if we do not use another smart pointer tohold the pointer returned from release, our program takes over responsibilityfor freeing that resource:
p2.release(); // WRONG: p2 won't free the memory and we'velost the pointer
auto p = p2.release(); // ok, but we mustremember to delete(p)
There is one exception to the rule that wecannot copy a unique_ptr: We can copy orassign a unique_ptr that is about to be destroyed. The most common example iswhen we return a unique_ptr from a function:
unique_ptr<int> clone(int p) {
//ok: explicitly create a unique_ptr<int> from int*
return unique_ptr<int>(new int(p));
}
unique_ptr<int> clone(int p) {
unique_ptr<int> ret(new int (p));
// .. .
return ret;
}
// p points to an object of type objT anduses an object of type delT to free that object
// it will call an object named fcn of typedelT
unique_ptr<objT, delT> p (new objT,fcn);
void f(destination &d /* other neededparameters */)
{
connection c = connect(&d); // open the connection
//when p is destroyed, the connection will be closed
unique_ptr<connection,decltype(end_connection)*>
p(&c, end_connection);
//use the connection
//when f exits, even if by an exception, the connection will be properly closed
}
14weak_ptr
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); // wp weakly shares with p; use count in p isunchanged
if (shared_ptr<int> np = wp.lock()) {// true if np is not null
//inside the if, np shares its object with p
}
15Dynamic Arrays
1 Best Practices:Mostapplications should use a library container rather than dynamically allocatedarrays. Using a container is easier, less likely to contain memory managementbugs, and is likely to give better performance.
2 typedef int arrT[42]; // arrT names thetype array of 42 ints
int *p = new arrT; // allocates an array of 42 ints; p points tothe first one
delete [] p; // brackets are necessary because weallocated an array
// up points to an array of tenuninitialized ints
unique_ptr<int[]> up(new int[10]);
up.release(); // automatically uses delete[] to destroy itspointer
Unlike unique_ptr, shared_ptrs provide nodirect support for managing a dynamic array. If we want to use a shared_ptr tomanage a dynamic array, we must provide our own deleter:
// to use a shared_ptr we must supply adeleter
shared_ptr<int> sp(new int[10],[](int *p) { delete[] p; });
sp.reset(); // uses the lambda we suppliedthat uses delete[] to free the array
// shared_ptrs don't have subscriptoperator and don't support pointer arithmetic
for (size_t i = 0; i != 10; ++i)
*(sp.get() + i) = i; // use get to get a built-in pointer
16 Theallocator Class
//int n = 3;
//string *const p = new string
;
//string str;
//string *q = p;
//while (cin >> str && q != p + n)
//{
// *q = str;
// ++q;
//}
//const size_t size = q - p;
//delete[]p;
allocator<string>salloc;
autop=salloc.allocate(3);
autoq=p;
stringstr;
while(cin>>str&&q!=p+3)
{
salloc.construct(q,str);
++q;
}
constsize_tsize=q-p;
q=p;
while(q!=p+3)
{
cout<<*q<<"
";
++q;
}
cout<<std::endl;
while(q!=p)
{
salloc.destroy(--q);
}
salloc.deallocate(p,3);
allocator<string> alloc; // object that can allocate strings
auto const p = alloc.allocate(n); //allocate n unconstructed strings
auto q = p; // q will point to one past thelast constructed element
alloc.construct(q++); // *q is the empty string
alloc.construct(q++, 10, 'c'); // *q is cccccccccc
alloc.construct(q++, "hi"); // *q is hi!
Warning: We must construct objects in order to use memory returned by allocate.Using unconstructed memory in other ways is undefined.
while (q != p)
alloc.destroy(--q); // free the strings we actually allocated
Warning:We may destroy only elements that are actually constructed.
alloc.deallocate(p, n);
The pointer we pass to deallocate cannot benull; it must point to memory allocated by allocate. Moreover, the sizeargument passed to deallocate must be the same size as used in the call toallocate that obtained the memory to which the pointer points.
相关文章推荐
- 《C++ Primer》读书笔记-第二章 01 数据的内存表示
- C++ Primer学习---静态与动态内存发配
- c++ primer(第五版)学习笔记及习题答案代码版(第十二章)动态内存与智能指针
- 论C/C++函数间动态内存的传递
- 《c++ primer》读书笔记12
- 《C++ Primer 3rd edition》读书笔记 - 第一篇 C++概述
- c++ Primer 3读书笔记(一)
- 《c++ primer》读书笔记11
- 《c++ primer》读书笔记 说明
- 从游戏中得到动态内存数据
- 论C/C++函数间动态内存的传递
- 从游戏中得到动态内存数据(汇编+VC 例子:疯狂坦克的X坐标)
- SQL服务器内存有两种基本管理方法:动态分配和静态分配
- 《C++ Primer 4th Edition》读书笔记 - Chapter1 Getting Started
- 《高质量C编程指南》读书笔记之内存管理 [转]
- 《C++ Primer 3rd edition》读书笔记 - 第二篇 基本语言
- 《c++ primer》读书笔记3
- 《c++ primer》读书笔记10
- 论C/C++函数间动态内存的传递
- 读书笔记-C++ Primer 7.3 Returning a Reference