dynamic memory
Dynamic memory and smart pointers
- C++ uses
new
anddelete
manages dynamic memory, but sometimes it is difficult to ensure the accuracy of memory allocation or release. In response to this problem, the standard library provides smart pointers, which can automatically release the pointed object,shared_ptr
allowing multiple pointers to point to the same object.
shared_ptr
- When copying or assigning, each
shared_ptr
will record how many othershared_ptr
objects point to the same object. It can be considered that eachshared_ptr
has an associated counter, usually called a reference count . When one is copiedshared_ptr
, the reference count will be incremented, andshared_ptr
when an object is destroyed , its corresponding reference count is decremented. shared_ptr
Automatic destruction of managed objects is achieved through the object's destructor .shared_ptr
The associated memory is also automatically freed.- A common reason to use dynamic memory is to allow multiple objects to share the same state. The commonly used container is to generate a new container by copying. If multiple containers are allowed to share the same elements, the cost of copying can be saved, which can be realized by using smart pointers.
code
void test() { // 使用智能指针的方式,sps会首先指向一个默认初始化的string shared_ptr<string> sps; if (sps && sps->empty()) { *sps = "233"; cout << *sps << endl; } shared_ptr<int> p1 = make_shared<int>(42); shared_ptr<string> p2 = make_shared<string>(10, 'A'); // make_shared可以提供特定类型的初始化参数 shared_ptr<int> p3 = make_shared<int>(); cout << *p1 << endl; cout << *p2 << endl; cout << *p3 << endl; shared_ptr<vector<string>> pstr1 = make_shared < vector<string> >(3, "2333"); cout << pstr1.use_count() << endl; // 1次 shared_ptr<vector<string>> pstr2 = pstr1; pstr2->push_back("test"); // 指向同一位置,所以修改pstr2,pstr1也会改变 for_each(pstr1->begin(), pstr1->end(), [](const string& str){ cout << str << endl; }); cout << pstr1.use_count() << endl; // 2次 }
direct memory management
- Use new and delete to manage memory directly
- new is used to dynamically allocate memory, and delete is used to release memory space
- When new, if the application for memory space fails,
bad_alloc
an exception will be reported, so it can be usednothrow
. When the application for memory space fails, a null pointer is returned without an exception being reported. - new and delete need to be used in pairs, otherwise the memory space applied for cannot be released.
- There are several major problems with using new and delete to manage memory
- Often forget to delete memory
- use a freed object
- The same block of memory is freed twice
- In view of the above problems, it is recommended to use smart pointers in use.
code
void test() { string s; // string *ps; // *ps = "233"; // 这种方法需要首先对指针进行赋值,否则会报错 string *p1 = new string; *p1 = "test"; cout << *p1 << endl; delete p1; // 必须手动释放内存 int *p2 = new int(42); delete p2; // 如果出现了内存不够导致无法分配内存的情况,可以使用nothrow,如果分配失败,则会返回空指针 int *p3 = new (nothrow) int(50); cout << *p3 << endl; const string* p4 = new string("hello"); // *p4 = "ssss"; // 因为是const对象,无法修改其值,会报错 delete p4; }
shared_ptr combined with new
If the smart pointer is not initialized, it will be initialized to a null pointer, and a built-in pointer can be converted to a smart pointer by direct initialization . Note: Such initialization cannot be achieved by the method of hermit conversion
code
void test() { // shared_ptr<int> p1 = new int(4); // 无法通过这样的方法实现隐式转换 shared_ptr<int> p2( new int(42) ); // 可以通过直接初始化的方式实现智能指针的初始化 cout << *p2 << endl; }
get
- get returns a built-in pointer to the object managed by the smart pointer.
- It is not recommended to use this property because it may mess up memory space management.
code
void another(int *p) { shared_ptr<int> ret( p ); } void test() { shared_ptr<int> p1( new int(5) ); another(p1.get() ); // p1所对应的内存空间会在another函数中被释放,下面的操作在运行的时候会出现错误 //int num = *p1; //cout << num << endl; }
Customize the action when the smart pointer is released
- The smart pointer can call the destructor by itself to release resources. We can also specify the deletion operation of this smart pointer object, and we need to pass in a pointer object, the type is the data type. Even if it is an exception, a custom function will be called to release resources
code
void freed(int *p) { delete p; cout << "p is being deleted" << endl; } void test() { // 自定义析构的函数 shared_ptr<int> p1( new int(5), freed ); // throw out_of_range("2333"); // 即使是出现异常,也可以调用freed进行资源释放 }
unique_ptr
- The previous shared_ptr is a method of reference counting, allowing multiple variables to use the same block address, and
unique_ptr
can only point to a given object, which can be directly initialized by using new - unique_ptr does not support copy and assignment
unique_ptr
The ownership of a pointer can be transferred from one to another through the release and reset methodsunique_ptr
.code
void test() { unique_ptr<int> p1(new int(4)); cout << *p1 << endl; //不支持拷贝与赋值 // unique_ptr<int> p2( p1 ); // unique_ptr<int> p2 = p1; unique_ptr<int> p2( p1.release() ); // release:p1放弃对指针的控制权,将p1置为空,同时返回该指针 cout << (p1 == NULL ? "p1 is NULL" : "p1 is not NULL") << endl; cout << *p2 << endl; p2.reset(); // 释放了p2所指的内存,同时将p2置为空 cout << (p2 == NULL ? "p2 is NULL" : "p2 is not NULL") << endl; }
deleter
- By default,
unique_ptr
bothshared_ptr
use delete to release the object it refers to; at the same time, a deleter can also beunique_ptr
passed in, but the usage methodshared_ptr
is different, he needs to pass in 2 type templates code
void freed(int *p) { delete p; cout << "p is being deleted" << endl; } void test() { // 可以使用decltype进行函数指针的声明,方便一些 //unique_ptr<int, decltype(freed)*> p1( new int(4), freed ); unique_ptr<int, void(*)(int*)> p1(new int(4), freed); cout << *p1 << endl; }
weak_ptr
- Rarely used, not introduced here
dynamic array
- The objects allocated by new, whether individually allocated or in an array, are initialized by default. You can add a pair of empty parentheses after the definition to implement value initialization.
- It is legal to dynamically allocate an array of size 0, but the array cannot be dereferenced.
- The way to release the array can be used
delete [] p
, if it is a single value,delete p
you can use it. The standard library provides a
unique_ptr
version that can manage arrays allocated by new. When the smart pointer is released, it will calldelete [] p
to release the array resources, butshared_ptr
there is no such operation. If it needs to manage the array, we need to customize the deletervoid test() { int *p1 = new int[10]; //没有初始化 int *p2 = new int[10](); // 初始化值为0 int *p3 = new int[10]{1,2,3,4}; cout << *p1 << ", " << *p2 << endl; for (int i = 0; i < 10; i++) cout << p3[i] << " "; cout << endl; int *p4 = new int[0](); // 初始化大小为0没有问题 //cout << *p4 << endl; //在这里解引用会出现意想不到的结果 delete[] p1; delete[] p2; delete[] p3; delete[] p4; cout << "unique_ptr\n"; unique_ptr<int[]> p5(new int[10]{1,2,3,4,5,6}); for (int i = 0; i < 10; i++) cout << p5[i] << " "; cout << endl; cout << "shared_ptr\n"; shared_ptr<int> p6(new int[10]{8, 7, 6, 5, 4}, [](int *p){ cout << "user defined delete func for shared_ptr" << endl; delete[] p; }); for (int i = 0; i < 10; i++) cout << *(p6.get()+i) << " "; // 没有实现上面的下标引用访问的方式 cout << endl; }