C++ Study Notes (12) - Dynamic Memory, Smart Pointers and Dynamic Arrays
Objects used in programs have strictly defined lifetimes.
- Global object: allocated when the program starts, destroyed when the program ends
- Local automatic objects: created when the program enters the block in which it is defined, and destroyed when it leaves the block
- Local
static
objects: allocated before first use, destroyed when program ends - Dynamically allocate objects : show creation, show release
memory storage area
- Static memory: local
static
objects, classstatic
data members, and variables defined outside any function static
Stack memory: non- objects defined within functions- Free space (heap): dynamically allocated objects, the lifetime of dynamically allocated objects is controlled by the program
Dynamic memory and smart pointers
The management of dynamic memory is done through a pair of operators
- new : Allocate space for an object in dynamic memory and return a pointer to the object, which is used to initialize the object
- delete : accepts a pointer to a dynamic object, destroys the object, and frees the memory associated with it
There is a problem with the use of dynamic memory in the container
- Memory leak: forgetting to free memory
- Illegal memory pointer: The village is released when there are still pointers to refer to the memory
The new standard library provides two types of smart pointers to manage dynamic objects. The difference between smart pointers and regular pointers is that they are responsible for automatically releasing the objects they point to.
shared_ptr
: allow multiple pointers to the same objectunique_ptr
: exclusive to the object pointed toweak_ptr
: a weak reference that points toshared_ptr
the managed object
shared_ptr
kind
Create a smart pointer
shared_ptr<string> p1; // shared_ptr, 可以指向 string
shared_ptr<list<int>> p2; // shared_ptr, 可以指向 int 的 list
make_shared
function
make_shared
Allocate a formation in dynamic memory and initialize it, returning a pointer to this object shared_ptr
, using its arguments to construct an object of the given type
// 指向一个值为“9999999999” 的 string
shared_ptr<string> p4 = make_shared<string>(10, '9');
// 也可使用 auto, p6 指向一个动态分配的空 vector<string>
auto p6 = make_shared<vector<string>>();
reference counting
- Increment counter while copying
- one
shared_ptr
initializes the othershared_ptr
- will
shared_ptr
be passed as a parameter to the function - will
shared_ptr
be the return value of the function
- one
- Counter decrements
- When assigning
shared_ptr
a new value to shared_ptr
On destruction (such as when a localshared_ptr
object leaves its scope)
- When assigning
auto r = make_shared<int>(42); // r 指向的 int 只有一个引用者
r = q; // 给 r 赋值,令它指向另一个地址
// 递增 q 指向的对象的引用计数
// 递减 r 原来指向的对象的引用计数
// r 原来指向的对象已没有引用者,会自动释放
shared_ptr
Destroy and free memory
shared_ptr
The destructor will decrement the reference count of the object it points to, and if the reference technology changes 0
, shared_ptr
the destructor will destroy the object and free the memory it occupies
void function(T arg)
{
shared_ptr<int> p = make_shared<int>(125);
// 情况1:不返回, p 为局部变量,函数结束时被销毁,p被销毁时,引用计数递减为0,故内存被释放
return p; // 情况2:返回 p 的拷贝, 引用计数进行递增操作,故即使 p 被销毁,但内存还有其他使用者,不会被释放
}
Reasons to use dynamic memory
- The program does not know how many objects it needs to use, such as container classes
- The program does not know the exact type of the desired object
- Programs need to share data among multiple objects
direct memory management
The C++ language defines two operators to allocate and release dynamic memory. Compared with smart pointers, it directly manages container errors.
new
: Allocate memory and return a pointer to the object.// 默认情况下,动态分配对象是默认初始化的,即内置类型或组合类型的对象的值将未定义,类使用默认构造函数 int *pi = new int; // pi 指向一个动态分配的、未初始化的无名对象,即默认初始化,`*pi值未定义 // 也可对动态分配对象进行值初始化,只需在类型名之后跟一对控括号 int *pi2 = new int(); // 值初始化为0, *pi2 为0 ///若括号中仅有单一初始化器,可使用 auto 推断想要分配的对象类型 auto p1 = new auto(obj); // p1类型是指针,指向从 obj 自动推断出的类型 auto p2 new auto{a, b, c}; // 错误:括号中只能有单个初始化器 //用 new 分配 const 对象是合法的, 下为分配并初始化一个 const int const int *pci = new const int(1024);
delete
: Destroy the object pointed to by the given pointer and release the corresponding memory. Must be a pointer to dynamically allocated, or a null pointer. Freeing a blocknew
of memory that was not allocated, or freeing the same pointer multiple times, has undefined behaviorEmpty dangling town : pointer to a block of memory that once held data objects but is now invalid
shared_ptr
and new
use in combination
Smart pointer constructor that accepts pointer parameters Yes
explicit
, so you can't say that a built-in pointer is implicitly converted to a smart pointer, you must use direct initializationshared_ptr<int> p1 = new int(1024); // 错误:必须使用直接初始化方式 shared_ptr<int> p2(new int(1024)); // 正确:使用了直接初始化方式
Do not mix normal pointers and smart pointers, because it is dangerous for built-in pointers to access the object the smart pointer is responsible for, because there is no way to know when the object will be destroyed
Do not use to
get
initialize another smart pointer or assign a value to a smart pointer.get
Defined in the smart pointer type, usher returns a built-in pointer to the object managed by the smart pointer.shared_ptr<int> p(new int(42)); // 引用计数为 1 int *q = p.get(); // 正确:但使用 q 时要注意,不要让他管理的指针被释放 {// 新程序块 // 未定义:两个独立的`shared_ptr`指向相同的内存 shared_ptr<int>(q) }// 程序块结束,q 被销毁,它指向的内存被释放 int foot = *p; // 未定义:p 指向的内存已经被释放了
Smart pointers and exceptions
Smart pointers are still properly freed when exceptions occur, but directly managed memory is not freed automatically
void f() { shared_ptr<int> sp(new int(42)); // 分配一个新对象 int *ip = new int(42); // 动态分配一个新对象 // 这段代码抛出一个异常,且在 f 中未被捕获 delete ip; // 在推出之前释放内存,发生异常时,没有执行,被跳过 } // 在函数结束时 shared_ptr 自动释放内存
Deleter :
shared_ptr
Release the pointer saved in , which is used to replaceshared_ptr
the default operation when it is destroyed, and can be specified when it isdelete
createdshared_ptr
Continuation of this specification for the correct use of smart pointers
- Do not initialize (or
reset
) multiple smart pointers with the same built-in pointer value delete``get()
pointer that does not return- does not use
get()
initialization orreset
another smart pointer - If you use
get()
the returned pointer, remember that it will be invalidated when the last corresponding smart pointer is destroyed - If the resource managed by the smart pointer represents
new
allocated memory, remember to pass it a deleter
- Do not initialize (or
unique_ptr
unique_ptr
Owns the object it points to, that is, there can only be one unique_ptr
pointer to an object at a time, and when it is destroyed, the pointed object is also destroyed
unique_ptr
No similarmake_shared
function. When defined, it needs to be bound to thenew
returned pointerSimilarly
shared_ptr
, initializationunique_ptr
needs to be in the form of direct initializationunique_ptr
Exclusively the object it points to, so it does not support ordinary copy or assignment operationsunique_ptr<string> p1(new string("hao")); // p2 指向一个值为 hao 的 string unique_ptr<string> p2(p1); // 错误:unique_ptr 不支持拷贝 unique_ptr<string> p3; p3 = p2; // 错误:unique_ptr 不支持赋值
unique_ptr
Although it cannot be assigned or copied, it is possible to callrelease
orreset
transfer ownership fromconst
oneunique_ptr
to anotherunique_ptr
// 接上 unique_ptr<string> p2(p1.release()); // release 将 p1 置为空,并将所有权转移给 p2
unique_ptr
Can be copied or assigned when it will be destroyed, such as returning from a functionunique_ptr
unique_ptr<int> clone(int p) { return unique_ptr<int>(new int(p)); }
Similarly
shared_ptr
,unique_ptr
its default deleter can also be overloaded// p 指向一个类型为 objT 的对象,并使用一个类型为 delT 的对象释放 objT对象 // 它会调用一个名为 fcn 的 delT 类型对象 unique_ptr<objT, delT> p (new objT, fcn);
weak_ptr
A smart pointer that does not control the lifetime of the pointed-to object, pointing to an shared_ptr
object managed by a. shared_ptr
and reference counts that do not change after binding
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); // wp 弱共享 p; p 的引用计数未改变
// 由于对象可能不存在,故不能使用 weak_ptr 直接访问队形,而必须调用 lock
if(shared_ptr<int> np = wp.lock()) // 如果 np 不为空则条件成立
{
// 在 if 中, np 与 p 共享对象
}
dynamic array
The C++ language and the standard library provide two methods for allocating an array of objects one at a time.
- The C++ language defines another
new
expression syntax that allocates and initializes an array of objects - The standard library provides
allocator
classes that allow allocation and initialization separation, better performance and more flexibility
new
and array
new
allocating arrayint *pia = new int[5]; // pia 指向第一个 int typedef int arrT[42]; // arrT 表示 42 个 int 的数组类型 int *p = new arrT; // 分配一个 42 个int 的数组; p 指向第一个 int
Although the allocated memory is usually called
new T[]
a dynamic array, it does not get an array type object, but a pointer to an array element type, that is, a dynamic array is not an array type, and certain functions such asbegin
,end
or rangefor
statements cannot be used.By default,
new
allocated objects are default-initialized. You can also perform value initialization, just add it()
afterint *pia = new int[10]; // 10 个未初始化的 int int *pia2 = new int[10](); // 10 个值初始化为 0 的 int int *pia3 = new int[10]{0, 1, 2, 3, 4, ,5}; // 列表初始化,剩余元素执行值初始化
An initializer cannot be given in parentheses, i.e.
auto
an array cannot be allocated withWhen freeing a dynamic array, add an empty square bracket before the pointer, even if the array is defined with a type alias, the elements are destroyed in reverse order
typedef int arrT[42]; // arrT 表示 42 个 int 的数组类型 int *p = new arrT; // 分配一个 42 个int 的数组; p 指向第一个 int delete [] p; // 方括号必需,因为分配的是一个数组
The standard library provides a version
new
of the array that manages allocationsunique_ptr
unique_ptr<int[]> up(new int[10]); // up 指向一个包含10个未初始化 int 的数组 up.release(); // 自动用 delete[] 销毁其指针
If you want to use
shared_ptr
managed dynamic arrays, you must provide a custom deletershared_ptr<int> sp(new int[10], [](int *p){delete[] p;}); // 提供自定义删除器 sp.reset(); // 使用自定义的 lambda 释放数组,它使用 delete[]
allocator
kind
allocator
A class separates memory allocation from object construction, and its allocated memory is raw, unconstructed.
allocator<string> alloc; // 可以分配 string 的 allocator 对象
auto const p = alloc.allocate(n); // 分配 n 个未初始化的 string
auto q = p; // q 指向最后构造的元素之后的位置
alloc.construct(q++); // *q 为空字符串
alloc.construct(q++, 10, 'c'); // *q 为 cccccccccc
alloc.construct(q++, "hi"); // *q 为 hi
while(q != p)
alloc.destroy(--q); // 释放我们真正构造的 string
alloc.deallocate(p, n); // 释放内存
In order to use
allocator
the returned memory, theconstruct
object must be constructed with bit-constructed memory, behavior is undefinedAfter use, must be called on each constructed element
destroy
to destroy,destroy
accept a pointer, and perform a destructor on the pointed objectAfter destruction, this part of the memory can be reused to save others
string
, or the memory can be released and returned to the systemAlgorithms for copying and filling uninitialized memory
vector<int> vi{1, 2, 3}; allocator<int> alloc; auto p = alloc.allocate(vi.size() * 2); // 分配比 vi 中元素所占空间大一倍的动态内存 auto q = alloc.unintialized_copy(vi.begin(), vi.end(), p); //拷贝vi中元素构造从p开始的元素 uninitialized_fill_n(q, vi.size(), 42); // 将剩余元素初始化为42
Epilogue
In C++, memory is allocated dynamic memory blocks through the new 表达式分配,通过
delete class表达式释放。标准库还定义了一个``allocator
C++ programs should now use smart pointers whenever possible, because direct management is error-prone