头文件:#include <memory>
(1) 智能指针shared_ptr:
①不用手动释放对象占用的内存;
②避免浅拷贝带来的内存重复释放问题。
③get函数用于返回指向的实际对象的指针,基本上等同于->操作符;
std::shared_ptr<Abase> sp(new Abase());
A* pa = sp.get();
pa->print(); // 类中的方法
④实现了*和->方法,因此可以操作实际对象。
⑤可使用use_count()来查看当前对象被引用的次数;
⑥可以使用make_shared创建shared_ptr对象,其中make_shared后面括号内的参数为选择要构造的实际对象的构造函数中的参数;
shared_ptr<Abase> A = make_shared<Abase>();
shared_ptr<Abase> A = make_shared<Abase>(“AAAAAA”);
(2) 弱引用指针weak_ptr:
①weak_ptr用于辅助shared_ptr的,只能指向shred_ptr对象,而不能直接指向实际对象本身,如
weak_ptr<Abase> ptr (new Abase()); // 编译器会报错;
②弱引用不能操作对象,因为没有实现operator* 和 operator->方法;
③可以将shared_ptr对象或者另一个weak_ptr对象赋值给weak_ptr对象,但赋值后不会增加引用计数;
④weak_ptr中只有函数lock和expired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,如果expired() == true(函数expired()的功能等价于use_count()==0),表示实际对象已经不复存在了,这个时候调用lock返回的是一个指向null的shared_ptr对象;如果expired() == false,可以使用lock函数来获取其对应的shared_ptr对象(lock()会创建shared_ptr指针,在该指针的作用域范围内,会使引用次数+1,出作用域,引用次数-1)。
⑤weak_ptr常用来解决循环引用带来的无法内存释放问题;
⑥也可使用use_count()来查看当前对象的弱引用次数(没人会关心弱引用计数的次数,因为实际对象释放与否,是由智能指针引用次数决定的);
示例代码:
#include <iostream>
#include <memory>
using namespace std;
class base {
public:
base(string name) : mName(name) {
cout << "construct " << mName << endl;
}
virtual ~base() {
cout << "deconstruct " << mName << endl;
}
void print() {
cout << mName << " will print" << endl;
}
public:
string mName;
};
class Abase {
public:
Abase(string name) : mName(name) {
cout << "construct " << mName << endl;
}
virtual ~Abase() {
cout << "deconstruct " << mName << endl;
}
void print() {
cout << mName << " will print" << endl;
}
public:
string mName;
};
int main(int argc, char* argv[]) {
{
shared_ptr<Abase> A(new Abase("AAAAAAA"));
// 或者用make_shared来构造
// shared_ptr<Abase> A = maek_shared<Abase> ("AAAAAAA");
shared_ptr<base> B(new base("BBBBBBB"));
// 返回指向实际对象的普通指针
Abase* ptr = A.get();
ptr->print();
cout << B.use_count() << endl;
cout << A.use_count() << endl;
weak_ptr<Abase> weaka = A;
weak_ptr<base> weakb = B;
{
// 获取shared_ptr对象,这会使实际对象的引用次数+1,出作用域后引用计数-1
shared_ptr<Abase> weakaLock = weaka.lock();
weakaLock->print();
cout << A.use_count() << endl;
}
if (!weakb.expired()) {
// 将获取的shared_ptr对象赋值给临时变量,在这句话结束以后临时变量资源被回收,
// 因此引用计数不会+1
weakb.lock()->print();
cout << B.use_count() << endl;
}
// 将weakb指向的对象清空
weakb.reset();
if (weakb.expired()) {
cout << "weakb is invalid" << endl;
}
cout << B.use_count() << endl;
cout << A.use_count() << endl;
}
system("pause");
return 0;
}
日志输出:
construct AAAAAAA
construct BBBBBBB
AAAAAAA will print
1
1
AAAAAAA will print
2
BBBBBBB will print
1
weakb is invalid
1
1
deconstruct BBBBBBB
deconstruct AAAAAAA
接下来看看使用weak_ptr最多的场景,解决循环引用带来的内存泄漏问题,实例如下:
#include <iostream>
#include <memory>
using namespace std;
class Abase;
class base {
public:
base(string name): mName(name) {
cout << "construct " << mName << endl;
}
virtual ~base() {
cout << "deconstruct " << mName << endl;
}
void print() {
cout << mName << " will print" << endl;
}
public:
string mName;
shared_ptr<Abase> base_ptr;
};
class Abase {
public:
Abase(string name):mName(name){
cout << "construct " << mName << endl;
}
virtual ~Abase() {
cout << "deconstruct " << mName << endl;
}
void print() {
cout << mName << " will print" << endl;
}
public:
string mName;
shared_ptr<base> Abase_ptr;
};
int main(int argc, char* argv[]) {
{
shared_ptr<Abase> A(new Abase("AAAAAAA"));
shared_ptr<base> B(new base("BBBBBBB"));
cout << B.use_count() << endl;
cout << A.use_count() << endl;
A->Abase_ptr = B;
B->base_ptr = A;
cout << B.use_count() << endl;
cout << A.use_count() << endl;
}
system("pause");
return 0;
}
日志输出:
construct AAAAAAA
construct BBBBBBB
1
1
2
2
离开作用域后Abase,base对象的引用计数变为1,因此不会触发析构函数,这就导致了内存泄漏。因为weak_ptr不会增加引用计数,所以只要将Abase或base中的指针类型改成weak_ptr即可,此时日志输出:
construct AAAAAAA
construct BBBBBBB
1
1
1
2
deconstruct BBBBBBB
deconstruct AAAAAAA
再介绍下unique_ptr指针:
unique_ptr指针,是一个装指针的容器,且拥有关联指针的唯一所有权(内部没有引用计数)。当unique_ptr对象超出作用域时会自动析构,其析构函数中会delete其关联指针,这样就相当于替我们执行了delete堆内存上的对象。
创建unique_ptr对象有两种方法:
//C++11:
std::unique_ptr<Abase> A_ptr(new Abase("AAAAAAA"));
//C++14:
std::unique_ptr<Abase> A_ptr = std::make_unique<Abase>("AAAAAAA");
使用unique_ptr规则:
① unique_ptr不能直接复制,必须使用std::move()
转移其管理的指针,转移后原 unique_ptr 为nullptr:
// 转移后 A_ptr2 == nullptr
std::unique_ptr<Abase> A_ptr1 = std::move(A_ptr2);
② 使用if(ptr1 == nullptr)来判断unique_ptr对象是否为空,也就是查看其是否有关联原始指针:
if(ptr1 == nullptr)
③通过get()获取关联指针;通过reset()重置unique_ptr对象为空;通过release()返回获取关联指针,并释放关联指针的所有权,此时unique_ptr对象为nullptr。
如有遗漏后续补充,bye~