理论
-
概述
-
智能指针:把管理资源的责任交给了对象,这样我们在使用结束时不需要显式地释放资源,而是由析构函数自动完成这一工作
-
它是一个类模板,可以创建任意类型的指针对象
-
智能指针使用时,资源对象不能被重复释放,否则会导致未定义的行为
-
-
C++中的智能指针关系
智能指针 简述 auto_ptr
存在管理权转移问题,不适合共享资源,已被C++11弃用 unique_ptr
暴力处理 auto_ptr
的管理权转移问题,独占资源所有权,不允许拷贝和赋值操作(开销小)shared_ptr
使用引用计数优雅解决 auto_ptr
的问题,但带来了循环引用bug(开销大)weak_ptr
专注给 shared_ptr
打补丁,不增加引用计数 -
auto_ptr
-
不足
管理权转移,原对象拷贝给新对象时,原对象被置为
nullptr
,此时只有新对象指向这块资源空间此时如果再使用原来的对象,那么会出现未定义的行为(资源不能被重复释放)
-
-
unique_ptr
-
原理
针对
auto_ptr
在拷贝、赋值操作中的管理权转移问题,unique_ptr
直接禁用了拷贝和赋值操作这样自然就避免了重复释放资源
-
不足
不能进行拷贝和赋值操作
可以使用move把左值转成右值进行赋值操作,此时效果和
auto_ptr
一样move是一种将对象转变为右值引用的方法:表示将放弃当前对象的所有权,将所有权转移到另一个对象,由新对象接管所有权
-
-
shared_ptr
-
原理
为了进行拷贝和赋值操作,
shared_ptr
采用了引用计数原理,记录有多少shared_ptr实例指向同一资源有几个实例对象计数值就是几,调用析构函数会将计数值减1,当计数为0时,才会释放资源
引用计数
指向的对象在堆上,保证了所有线程都能够访问该资源;shared_ptr
引用计数通过原子操作实现,因此引用计数本身是线程安全的但是,如果多线程尝试修改
引用计数
时,需要用户手动进行同步、加锁等操作以确保对资源的访问是安全的 -
不足
存在循环引用问题,导致引用计数不能变成0
两个类对象,二者通过
shared_ptr
智能指针互相引用,会增加引用计数
-
-
weak_ptr
-
原理
weak_ptr
对象可以指向shared_ptr
,但不会改变shared_ptr
的引用计数 -
不足
仅用于解决
shared_ptr
的循环引用问题,它不能直接访问资源
-
实验
auto_ptr
管理权转移
// auto_ptr管理权转移
#include <iostream>
#include <memory>
using namespace std;
class Test {
public:
Test(const string &name):obj_name(name){
std::cout << obj_name << "——构造函数.\n";
}
~Test(){
std::cout << obj_name << "——析构函数.\n";
}
private:
string obj_name;
};
int main() {
auto_ptr<Test> obj1(new Test("obj1"));
auto_ptr<Test> obj2(new Test("obj2"));
cout << "obj1 address = " << obj1.get() << endl;
cout << "obj2 address = " << obj2.get() << endl;
cout << "\n执行 obj2 = obj1\n" << endl;
obj2 = obj1;
cout << "obj1 address = " << obj1.get() << endl;
cout << "obj2 address = " << obj2.get() << endl;
return 0;
}
unique_ptr
独占资源
// unique_ptr独占资源
#include <iostream>
#include <memory>
using namespace std;
class Test {
public:
Test(const string &name):obj_name(name){
cout << obj_name << "——构造函数.\n";
}
~Test(){
cout << obj_name << "——析构函数.\n";
}
private:
string obj_name;
};
int main() {
unique_ptr<Test> obj1(new Test("obj1"));
unique_ptr<Test> obj2(new Test("obj2"));
cout << "obj1 address = " << obj1.get() << endl;
cout << "obj2 address = " << obj2.get() << endl;
cout << "\n执行 obj2 = move(obj1)\n" << endl;
// obj2 = obj1;
obj2 = move(obj1);
cout << "obj1 address = " << obj1.get() << endl;
cout << "obj2 address = " << obj2.get() << endl;
return 0;
}
shared_ptr
共享资源
// shared_ptr共享资源
#include <iostream>
#include <memory>
using namespace std;
class Test {
public:
Test(const string &name):obj_name(name){
cout << obj_name << "——构造函数.\n";
}
~Test(){
cout << obj_name << "——析构函数.\n";
}
private:
string obj_name;
};
int main() {
shared_ptr<Test> obj1(new Test("obj1"));
shared_ptr<Test> obj2(new Test("obj2"));
cout << "obj1 address = " << obj1.get() << endl;
cout << "obj2 address = " << obj2.get() << endl;
cout << "\n执行 obj2 = obj1\n" << endl;
obj2 = obj1;
cout << "obj1 address = " << obj1.get() << endl;
cout << "obj2 address = " << obj2.get() << endl;
return 0;
}
shared_ptr
循环引用
// shared_ptr循环引用
#include <iostream>
#include <memory>
using namespace std;
class A;
class B {
public:
shared_ptr<A> a_ptr;
B() { cout << "B类 构造函数" << endl; }
~B() { cout << "B类 析构函数" << endl; }
};
class A {
public:
shared_ptr<B> b_ptr;
A() { cout << "A类 构造函数" << endl; }
~A() { cout << "A类 析构函数" << endl; }
};
int main() {
// shared_ptr<A> a = make_shared<A>();
// shared_ptr<B> b = make_shared<B>();
shared_ptr<A> a(new A());
shared_ptr<B> b(new B());
a->b_ptr = b; // A 指向 B
b->a_ptr = a; // B 指向 A
// 循环引用导致对象不能被正确销毁
cout << "a use_count: " << a.use_count() << endl;
cout << "b use_count: " << b.use_count() << endl;
return 0;
}
weak_ptr
解决循环引用
// weak_ptr解决循环引用
#include <iostream>
#include <memory>
using namespace std;
class A; // 前向声明
class B {
public:
weak_ptr<A> a_ptr; // 使用 weak_ptr 来避免循环引用
B() { cout << "B类 构造函数" << endl; }
~B() { cout << "B类 析构函数" << endl; }
};
class A {
public:
// shared_ptr<B> b_ptr;
weak_ptr<B> b_ptr;
A() { cout << "A类 构造函数" << endl; }
~A() { cout << "A类 析构函数" << endl; }
};
int main() {
// shared_ptr<A> a = make_shared<A>();
// shared_ptr<B> b = make_shared<B>();
shared_ptr<A> a(new A());
shared_ptr<B> b(new B());
a->b_ptr = b;
b->a_ptr = a;
cout << "a use_count: " << a.use_count() << endl;
cout << "b use_count: " << b.use_count() << endl;
return 0;
}