NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
# Chapter 12. Dynamic Memory Our programs have used only static or stack memory. Static memory is used for local static objects, for class static data members, and for variables defined outside any function. Stack memory is used for non-static objects defined inside functions. Objects allocated in static or stack memory are automatically created and destroyed by the compiler. Stack objects exist only while the block in which they are defined is executing; static objects are allocated before they are used, and they are destroyed when the program ends. In addition to static or stack memory, every program also has a pool of memory that it can use. This memory is referred to as the **free store** or **heap**. Programs use the heap for objects that they **dynamically allocate**, this is for objects that the program allocates at run time. ## 12.1 Dynamic Memory and Smart Pointers In C++, dynamic memory is managed through a pair of operators: **new**, which allocates, and optionally initialises, an object in dynamic memory and returns a pointer to that object; and **delete**, which takes a pointer to a dynamic object, destroys that object, and frees the associated memory. Dynamic memory is problematic because it is surprisingly hard to ensure that we free memory at the right time. To make using dynamic memory easier, the new library provides two smart pointer types that manage dynamic objects. A smart pointer acts like a regular pointer with the important exception that it automatically deletes the object to which it points. * **shared\_ptr**, which allows multiple pointers to refer to the same object. * **unique\_ptr**, which "owns" the object to which it points. * **weak\_ptr**, which is a weak reference to an object managed by a shared\_ptr. All three are defined in the `memory` header. ### 12.1.1 The shared\_ptr Class ```cpp shared_ptr<string> p1; ``` A default initialised smart pointer holds a null pointer. The `make_shared` function will allocate and initialise an object in dynamic memory and returns a `shared_ptr` that points to that object. ```cpp // shared_ptr that points to an int with value 42 shared_ptr<int> p2 = make_shared<int>(42); // points to a string with value 9999999999 shared_ptr<string> p3 = make_shared<string>(10, '9'); ``` When we copy or assign a `shared_ptr`, each `shared_ptr` keeps track of how many other `shared_ptr`s point to the same object. We can think of a `shared_ptr` as if it has an associated counter, usually referred to as a reference count. Whenever we copy a `shared_ptr`, the count is incremented. The counter is decremented when we assign a new value to the `shared_ptr` and when the `shared_ptr` itself is destroyed. Once a `shared_ptr`'s counter goes to zero, the shared\_ptr automatically frees the object that it manages. | Operations Common to shared\_ptr and unique\_ptr | Description | | :--- | :--- | | shared\_ptr&lt;T&gt; sp; | Null smart pointer that can point to objects of type T. | | unique\_ptr&lt;T&gt; up; | | | p | Use p as a condition; true if p points to an object. | | \*p | Dereference p to get the object to which p points. | | p-&gt;mem; | Synonym for \(\*p\).mem. | | p.get\(\); | Returns the pointer in p. | | swap\(p, q\); | Swaps the pointers in p and q. | | p.swap\(q\); | | It does so through another special member function known as a **destructor**. Analogous to its constructors, each class has a destructor, the destructor controls what happens when objects of that class type are destroyed. | Operations Specific to shared\_ptr | Description | | :--- | :--- | | make\_shared&lt;T&gt;\(args\); | Returns a shared\_ptr pointing to a dynamically allocated object type T. | | shared\_ptr&lt;T&gt; p\(q\); | p is a copy of the shared\_ptr q. | | p = q; | p and q are shared\_ptrs holding pointers that can be converted to one another. | | p.unique\(\); | Returns true if p.use\_count\(\) is one; false otherwise. | | p.use\_count\(\); | Returns the number of objects sharing with p. | Programs tend to use dynamic memory for one of three purposes: * They don't know how many objects they'll need. * They don't know the precise type of the objects they need. * They want to share data between several objects. ```cpp vector<string> v1; // empty vector { vector<string> v2 = {"a", "an", "the"}; v1 = v2; // copies the element from v2 into v1 } ``` The elements allocated by a vector exist only while the vector itself exits. When a vector is destroyed, the elements in the vector are also destroyed. Unlike the containers, we want Blob objects that are copies of one another to share the same elements. That is, when we copy a Blob, the original and the copy should refer to the same underlying elements. ```cpp Blob<string> b1; // empty Blob { Blob<string> b2 = {"a", "an", "the"}; b1 = b2; // b1 and b2 share the same element. } ``` ### 12.1.2 Managing Memory Directly The **new** returns a pointer to the object it allocates: ```cpp int *pi = new int; // default initialised, *pi is undefined. int *p1 = new int(); // value initialised to 0 ``` When we provide an initialiser inside parentheses, we can use auto to deduce the type to allocate, we can use auto only with a single initialiser inside parentheses: ```cpp auto p1 = new auto(obj); auto p2 = new auto{a, b, c}; // error: must use parentheses for the initialiser ``` It is legal to use new to allocate const object, a dynamically allocated const object must be initialised. ```cpp const int *pci = new const int(1024); ``` Once a program has used all of its available memory, **new** expressions will fail. By default, if new is unable to allocate the requested storage, it throws an exception of type `bad_alloc`. We can prevent new from throwing an exception by using a different form of **new**: ```cpp int *p1 = new int; int *p2 = new (nothrow) int; ``` A delete expression takes a pointer to the object we want to free: ```cpp delete p; ``` Deleting a pointer a memory that was not allocated by new, or deleting the same pointer value more than once, is undefined. The compiler will generate an error for the delete of `i` because it knows that `i` is not a pointer. Functions that return pointers to dynamic memory put a burden on their callers, the caller must remember to delete the memory: ```cpp Foo* factory(T arg) { return new Foo(arg); } void use_factory(T arg) { Foo *p = factory(arg); // use p but do not delete it } ``` After the delete, the pointer becomes what is referred to as a dangling pointer. A dangling pointer is one that refers to memory that once held an object but no longer does so. ### 12.1.3 Using shared\_ptrs with new We can also initialise a smart pointer from a pointer returned by new: ```cpp shared_ptr<int> p2(new int(42)); ``` The smart pointer constructors that take pointers are explicit. ```cpp shared_ptr<int> p1 = new int(1024); // error: must use direct initialisation shared_ptr<int> p2(new int(1024)); ``` | Other Ways to Define and Change shared\_ptrs | Description | | :--- | :--- | | shared\_ptr&lt;T&gt; p\(q\); | p manages the object to which the built-in pointer q points. | | shared\_ptr&lt;T&gt; p\(u\); | p assumes ownership from the unique\_ptr u. | | shared\_ptr&lt;T&gt; p\(q, d\); | p assumes ownership for the object to which the built-in pointer q points; p will uses the callable object d in place of delete. | | shared\_ptr&lt;T&gt; p\(p2, d\); | p is a copy of the shared\_ptr p2; p will uses the callable object d in place of delete. | | p.reset\(\); | If p is the only shared\_ptr pointing at its object, reset frees p's existing object. | | p.reset\(q\); | | | p.reset\(q, d\); | | The smart pointer types define a function named get that returns a built-in pointer to the object that the smart pointer is managing. ### 12.1.4 Smart Pointers and Exception When we use a smart pointer, the smart pointer class ensures that memory is freed when it is no longer needed even if the block is exited prematurely. When we create a shared\_ptr, we can pass an optional argument that points to a deleter function: ```cpp shared_ptr<connection> p(&c, end_connection); ``` ### 12.1.5 unique\_ptr A unique\_ptr "owns" the object to which it points. The object to which a `unique_ptr` points is destroyed when the `unique_ptr` is destroyed. Because a `unique_ptr` owns the object to which it points, `unique_ptr` does not support ordinary copy or assignment: ```cpp unique_ptr<string> p1(new string("Stegosaurus")); unique_ptr<strint> p2(p1); // error: no copy p3 = p2; // error: no assign ``` | unique\_ptr Operations | Description | | :--- | :--- | | unique\_ptr&lt;T&gt; u1; | Null unique\_ptrs that can point to objects of type T. | | unique\_ptr&lt;T, D&gt; u2; | | | unique\_ptr&lt;T, D&gt; u\(d\); | Null unique\_ptr that point to objects of type T that uses d, which must be an object type D in place of delete. | | u = nullptr; | Deletes the object to which u points. | | u.release\(\); | Relinquishes control of the pointer u had held; return the pointer u had held and makes u null. | | u.reset\(\); | Deletes the object to which u points. | | u.reset\(q\); | If the built-in pointer q is supplied, make u point to that object. | | u.reset\(nullptr\); | | Although we can't copy or assign a `unique_ptr`, we can transfer ownership from one `unique_ptr` to another by calling release or reset: ```cpp unique_ptr<string> p2(p1.release()); unique_ptr<string> p3(new string("Trex")); p2.reset(p3.release()); p2.release(); // WRONG: p2 won't free the memory and we've lost the pointer. ``` There is one exception to the rule that we cannot copy a `unique_ptr`: We can copy or assign a `unique_ptr` that is about to be destroyed. The most common example is when we return a `unique_ptr` from a function: ```cpp unique_ptr<int> clone(int p) { return unique_ptr<int>(new int(p)); } unique_ptr<int> clone(int p) { unique_ptr<int> ret(new int(p)); ... return ret; } ``` Overriding the deleter in a unique\_ptr affects the unique\_ptr type as well as how we construct objects of that type. We must supply the deleter type inside the angle brackets along with the type to which the unique\_ptr can point. ```cpp // p points to an object of type objT // and uses an object of type delT to free that objct // it will call an object named fcn of type delT. unique_ptr<objT, delT> p(new objT, fcn); ``` ### 12.1.6 weak\_ptr A `weak_ptr` is a smart pointer that does not control the lifetime of the object to which it points. Instead, a `weak_ptr` points to an object that is managed by a `shared_ptr`. Binding a `weak_ptr` to a `shared_ptr` does not change the reference count of the `shared_ptr`. | week\_ptrs | Description | | :--- | :--- | | weak\_ptr&lt;T&gt; w; | Null weak\_ptr that can point at objects of type T. | | weak\_ptr&lt;T&gt; w\(sp\); | weak\_ptr that points to the same object as the shared\_ptr sp. | | w = p; | p can be a shared\_ptr or a weak\_ptr. | | w.reset\(\); | Makes w null. | | w.use\_count\(\); | The number of shared\_ptrs that share ownership with w. | | w.expired\(\); | Returns true if w.use\_count\(\) is zero, false otherwise. | | w.lock\(\); | If expired is true, return a null shared\_ptr; otherwise returns a shared\_ptr to the object to which w points. | ## 12.2 Dynamic Arrays The library includes a template class named allocator that lets us separate allocation from initialisation. ### 12.2.1 new and Arrays ```cpp int *pia = new int[get_size()]; ``` The size inside the brackets must have integral type but need not be a constant. Under the new standard, we can also provide a braced list of element initialisers: ```cpp int *pia3 = new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; ``` It is legal to dynamically allocate an empty array: ```cpp char arr[0]; // error: cannot define a zero-length array. char *cp = new char[0]; // ok: but cp can't be dereferenced. ``` To free a dynamic array, we use a special form of delete that includes an empty pair of square brackets: ```cpp delete []pa; ``` The library provides a version of unique\_ptr that can manage arrays allocated via new. ```cpp unique_ptr<int[]> up(new int[10]); up.release(); ``` | unique\_ptrs to Arrays | Description | | :--- | :--- | | unique\_ptr&lt;T\[\]&gt; u; | u can point to a dynamically allocated array of the type T. | | unique\_ptr&lt;T\[\]&gt; u\(p\); | u points to the dynamically allocated array to which the built-in point p points. | | u\[i\] | Returns the object at position i in the array that u owns. | ### 12.2.2 The allocator Class The library **allocator** class, which is defined in the `memory` header, lets us separate allocation from construction. ```cpp allocator<stirng> alloc; auto const p = alloc.allocate(n); // allocate n unconstructred strings. ``` | Standard allocator Class and Customised Algorithm | Description | | :--- | :--- | | allocator&lt;T&gt; a; | Defines an allocator object named a that can allocate memory for objects of type t. | | a.allocate\(n\); | Allocates raw, unconstructed memory to hold n objects of type T. | | a.deallocate\(p, n\); | Deallocates memory that held n objects of type T starting at the address in the T\* pointer p. | | a.construct\(p, args\); | p must be a pointer to type T that points to raw memory; arg are passed to a constructor for type T, which is used to construct an object in the memory pointed to by p. | | a.destroy\(p\); | Runs the destructor on the object pointed to by the T\* pointer p. | The memory an allocator allocates is unconstructed. | allocator Algorithms | Description | | :--- | :--- | | uninitialised\_copy\(b, e, b2\); | Copies elements from the input range denoted by iterators b and e into unconstructed, raw memory denoted by the iterator b2. | | uninitialised\_copy\_n\(b, n, b2\); | Copies n elements starting from the one denoted by the iterator b into raw memory starting at b2. | | uninitialised\_fill\(b, e, t\); | Constructs objects in the range of raw memory denoted by iterators b and e as a copy of t. | | uninitialised\_fill\_n\(b, n, t\); | Constructs unsigned number n objects starting at b. | ```cpp auto p = alloc.allocate(vi.size() * 2); auto q = uninitialised_copy(vi.begin(), vi.end(), p); uninitialised_fill_n(q, vi.size(), 42); ```