您的位置:首页 > 编程语言 > C语言/C++

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

// 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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: