智能指针用法学习
转自:https://blog.csdn.net/cpp_learner/article/details/118912592,chatgpt
1.介绍
智能指针就是帮管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!使用智能指针可以自动调用对象的析构函数。
例子:
class Test { public: Test() { cout << "Test的构造函数..." << endl; } ~Test() { cout << "Test的析构函数..." << endl; } int getDebug() { return this->debug; } private: int debug = 20; }; int main() { //Test *test = new Test; shared_ptr<Test> test(new Test); cout << "test->debug:" << test->getDebug() << endl; cout << "(*test).debug:" << (*test).getDebug() << endl; return 0; } //输出结果 Test的构造函数... test->debug:20 (*test).debug:20 Test的析构函数...
智能指针可以像普通指针一样使用-> 和*取内容,是因为 shared_ptr重载了这2个操作符函数:
1.1 get方法
获取智能指针托管的指针地址。但一般不这样用,直接用智能指针操作即可。
// 定义智能指针 shared_ptr<Test> test(new Test); Test *tmp = test.get(); // 获取指针返回 cout << "tmp->debug:" << tmp->getDebug() << endl; // 函数原型 _NODISCARD _Ty * get() const noexcept { // return wrapped pointer return (_Myptr);// 直接返回托管的指针 }
1.2 release方法
取消智能指针对动态内存的托管。
// 定义智能指针 shared_ptr<Test> test(new Test); Test *tmp2 = test.release(); // 取消智能指针对动态内存的托管 delete tmp2; // 之前分配的内存需要自己手动释放 // 函数原型 _Ty * release() noexcept { // return wrapped pointer and give up ownership _Ty * _Tmp = _Myptr; _Myptr = nullptr;// 将成员指针设置为空 return (_Tmp);// 将指针返回外界,由外界来控制是否delete }
1.3 reset方法
重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉。
// 重置 p.reset() ; 将p重置为空指针,所管理对象引用计数 减1,若引用计数减少到0,空间会被销毁,会调用类的析构函数 p.reset(p1); 将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控,若引用计数减少到0,空间会被销毁,会调用类的析构函数 p.reset(p1,d); 将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器,若引用计数减少到0,空间会被销毁,会调用类的析构函数 p1是一个指针! // 交换 std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变 p1.swap(p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
2.shared_ptr 介绍
记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它。
class Person { public: Person(int v) { this->no = v; cout << "构造函数 \t no = " << this->no << endl; } ~Person() { cout << "析构函数 \t no = " << this->no << endl; } private: int no; }; int main() { shared_ptr<Person> sp1; shared_ptr<Person> sp2(new Person(2)); // 获取智能指针管控的共享指针的数量 use_count():引用计数 cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl << endl; // 共享 sp1 = sp2; cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl << endl; shared_ptr<Person> sp3(sp1); // 共同托管 cout << "sp1 use_count() = " << sp1.use_count() << endl; // 可以打印引用计数值 cout << "sp2 use_count() = " << sp2.use_count() << endl; cout << "sp2 use_count() = " << sp3.use_count() << endl << endl; return 0; } // 输出结果 构造函数 no = 2 sp1 use_count() = 0 sp2 use_count() = 1 sp1 use_count() = 2 sp2 use_count() = 2 sp1 use_count() = 3 sp2 use_count() = 3 sp2 use_count() = 3 析构函数 no = 2
2.1 使用make_shared构建对象
make_shared<类型>(构造函数参数列表); shared_ptr<int> up3 = make_shared<int>(2); // 多个参数以逗号','隔开,最多接受十个 shared_ptr<string> up4 = make_shared<string>("字符串"); shared_ptr<Person> up5 = make_shared<Person>(9); //()内是构造函数的参数
会将对象指针和引用计数指针分配在一起,更高效。
ref_count也需要一块堆空间,(这里vptr是什么?虚拟函数表的指针?为什么会放到这里?)
shared_ptr<Foo> x(new Foo);
这种方式需要为 Foo 和 ref_count 各分配一次内存,而使用
shared_ptr<Foo> x=make_shared<Foo>();
会一次分配较大的内存供 Foo 和 ref_count 对象容身,(存放到一块地址空间)。
3.注意事项
- 在调用p.release()时会返回该指针,但不会释放p所指的内存,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了。
- 禁止delete 智能指针get 函数返回的指针。析构时造成重复释放。
- 禁止用任何类型智能指针get 函数返回的指针去初始化另外一个智能指针。具体解释:
//有问题的代码 std::shared_ptr<int> sp1(new int(10)); // 管理一个动态分配的整数 std::shared_ptr<int> sp4(sp1.get()); // 直接使用裸指针构造
sp1.get()
返回一个指向动态分配的整数的裸指针,但 shared_ptr
并不共享控制块和引用计数信息。如果 shared_ptr
通过裸指针构造,两个 shared_ptr
将独立管理同一个指针对象。这会导致两个 shared_ptr
在各自的生命周期结束时尝试释放相同的动态分配对象,从而造成双重释放问题,导致未定义行为(通常程序崩溃)。正确用法:
auto sp1 = std::make_shared<int>(10); auto sp4 = sp1; // 正确: sp4 现在共享 sp1 的控制块和引用计数
通过拷贝构造函数来共享对象的所有权,确保引用计数正确管理对象的生命周期。
- 不要定义指向智能指针的指针,这违背了智能指针的初衷。
4.和const
4.1 const std::shared_ptr<T>
指针本身是常量,即你不能修改 shared_ptr 对象(例如,不能让它指向另一个对象),但通过这个指针可以修改它指向的对象。
const shared_ptr<string> up4 = make_shared<string>("mystr"); // 报错: error: no match for ‘operator=’ (operand types are ‘const std::shared_ptr<std::__cxx11::basic_string<char> >’ and ‘std::shared_ptr<std::__cxx11::basic_string<char> >’) up4 = make_shared<string>("mystr2");// 该行代码会报错,不能修改 `up4` 本身 *up4 = "mmmm"; cout<<*up4;// 输出mmmm,可以修改指向的内容
4.2 std::shared_ptr<const T>
指指针本身是可变的(即可以修改指针使其指向另一个对象),但不能修改它指向的对象,即所指对象是常量。当你希望通过 shared_ptr 提供只读访问权限时,可以使用 std::shared_ptr<const T>。
shared_ptr<const string> up4 = make_shared<string>("mystr"); up4 = make_shared<string>("mystr2");// 可以更改指向新的对象 //error: assing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers *up4 = "mmmm";//报错,不能修改 cout<<*up4;