内存管理优化
C++11提供智能指针来帮助管理内存,智能指针可以自动删除分配的内存
C++11提供三种智能指针:std::shared_ptr,std::unique_ptr,std::weak_ptr使用时需要引用头文件memory
C++11之前的auto_ptr
C++11之前的auto_ptr设计上有很多坑,若通过cory构造和 “=” 操作符赋值它们,被拷贝的指针会变成null ,而复制所得的指针将取得资源的唯一控制权。就轻轻松松把资源抢过来了。(太坏了)抢走了以后如果再去析构原来的内存肯定会有问题的。
所以C++11不建议使用auto_ptr,使用std::shared_ptr,std::unique_ptr这两个指针能实现auto_ptr的功能而且很安全
shared_ptr共享的智能指针
std::shared_ptr使用引用计数来实现内存共享的功能,当最后一个指向此内存的指针离开作用域以后,智能指针计数为0,这个内存才会被释放。
使用方式
- 初始化方式:
std::shared_ptr<int> p(new int(1)); // 构造函数初始化
std::shared_ptr<int> p2 = p;
std::shared_ptr<int> perr = new int(1); // 错误 不能直接赋值
std::shared_pte<int> p3;
p3.reset(new int(i)); // reset方法初始化
智能指针不能直接赋值初始化,需要通过构造函数初始化,或者reset方法初始化。
- 获取原始指针方式:
int* org = p.get();
- 指定删除器:
void Delete(int* p)
{
delete p;
}
std::shared_ptr<int> p(new int, Delete);
// 可以直接使用lambda表达式
std::shared_ptr<int> p(new int, [](int* p){ deletw p;});
注意问题
- 不能使用一个原始指针初始化多个shared_ptr
- 不要在函数实参中创建shared_ptr,因为不同环境函数调用约定以及函数的压栈顺序不同
- 通过shared_from_this()返回this指针,不要将this指针作为shared_ptr返回,this指针本质是一个裸指针,可能会造成重复析构
struct A
{
shared_ptr<A> getSelf()
{
return shared_ptr<A>(this);
}
};
int main()
{
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->getSelf();
shared_ptr<A> sp3 = sp1;
return 0;
}
上面的代码用A构造一个智能指针sp1,但是sp2是用同一个(this)指针构造两个智能指针,两个智能指针互相独立有各自的引用计数,如果离开作用域以后,各自的引用计数都-1,就会调用两次A的析构函数,然后析构同一块内存2次。。
对比sp3,sp3是跟sp1共享同一个智能指针体系,使用计数跟sp1是相同的。
可以看到三个指针指向同一个地址,但是指针引用计数不一样。
正确返回this指针的做法是,让目标类通过派生std::enable_shared_from_this类,使用成员函数shared_from_this返回this指针。
class A :public std::enable_shared_from_this<A>
{
public:
std::shared_ptr<A> getSelf()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->getSelf();
shared_ptr<A> sp3 = sp1;
}
这样就可以看到三个指针是同一个引用计数系统,enable_shared_from_this模板类里面是有一个weak_ptr指针,内部实现具体在weak_ptr讲解里。
- 避免循环引用,如果A中有B的shared_ptr,B中有A的shared_ptr,那同时AB的两个对象在同一作用域在离开作用域时,a和b的引用计数都不会-1导致两个对象都不被析构,解决方法是把一个shared_ptr改成weak_ptr
unique_ptr独占的智能指针
unique_ptr是一个独占型智能指针,不能共享,不允许直接赋值给另一个unique_ptr
使用方式
- 初始化:只能使用构造初始化C++11没有reset方法初始化,C++14会提供
unique_ptr<int> p(new(int));
unique_ptr<int> p2 = p; // 不允许直接赋值
- 指针转移:转移内存所有权
unique_ptr<int> p(new(int));
*p = 1;
std::cout <<"p: "<< p << " *p: " << *p << std::endl;
unique_ptr<int> p3 = std::move(p);
std::cout << "p: " << p /*<< *p */<< std::endl; // 错误p == nullptr *p崩溃
std::cout << "p3: " << p3 << " *p3: " << *p3 << std::endl;
转移所有权以后原来的指针P已经是nullptr了,输出结果:
- 指定删除器:跟shared_ptr不同的是需要确定删除器类型
void Delete(int* p)
{
delete p;
}
std::unique_ptr<int, void(*)(int*)> p(new int, Delete); //void(*)(int*) 指定删除器为函数指针
// 捕获变量的lambda表达式不能转换成函数指针,但可以转换为std::function
std::shared_ptr<int, std::function<void(int*)>> p(new int, [&](int* p){ deletw p;});
weak_ptr
弱指针weak_ptr用来监视shared_ptr的,不会使shared_ptr的引用计数增加,不会管理shared_ptr内部的指针,主要使为了监视shared_ptr的生命周期,不能共享资源,不能操作资源,构造不会增加引用计数,析构不会减少引用计数,可以用来返回this指针还有解决循环引用计数问题。
基本用法
- use_count()获取当前观测资源的引用计数
std::shared_ptr<int> p(new int(1));
std::shared_ptr<int> wp(p);
std::cout << wp.use_count() << std::endl; // 输出1
- expried() 判断当前资源是否已经释放
if(wp.expried())
std::cout <<"无效"<<std::endl;
else
std::cout <<"有效"<<std::endl;
- 通过lock()方法获取所监视的shared_ptr
std::weak_ptr<int> pw;
void f()
{
if (pw.expired()) //所监视的shared_ptr是否被释放
cout << "shared_ptr已释放 " << std::endl;
else
cout << "*shared_ptr = " << *pw.lock() << std::endl;
}
int main()
{
{
std::shared_ptr<int> sp = std::make_shared<int>(1);
pw = sp; // pw指针监视共享指针sp
f(); // sp未被释放
} // sp的作用域,sp离开这个作用域之后会被释放
f();// 新作用域sp已经被释放
}
- 使用weak_ptr返回this指针
上面将shared_ptr的时候讲了如何返回this指针:正确返回this指针的做法是,让目标类通过派生std::enable_shared_from_this类,使用成员函数shared_from_this返回this指针。
std::enable_shared_from_this 的内部可以看到里面有一个weak_ptr :_Wptr,当要获取类的shared_ptr的时候通过获取weak_ptr监视的shared_ptr获取的
template <class _Ty>
class enable_shared_from_this {
public:
shared_ptr<_Ty> shared_from_this() {
return shared_ptr<_Ty>(_Wptr);
}
shared_ptr<const _Ty> shared_from_this() const {
return shared_ptr<const _Ty>(_Wptr);
}
weak_ptr<_Ty> weak_from_this() noexcept {
return _Wptr;
}
weak_ptr<const _Ty> weak_from_this() const noexcept {
return _Wptr;
}
protected:
constexpr enable_shared_from_this() noexcept : _Wptr() {}
enable_shared_from_this(const enable_shared_from_this&) noexcept : _Wptr() {
}
enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept {
return *this;
}
~enable_shared_from_this() = default;
private:
friend class shared_ptr;
mutable weak_ptr<_Ty> _Wptr;
};
- 解决shared_ptr循环引用问题:将A或者B中的任意一个共享指针变成weak_ptr就可以了
struct A
{
std::shared_ptr<B> bptr;
~A()
{
std::cout << "~A" << std::endl;
};
};
struct B
{
std::weak_ptr<A> aptr;
~B()
{
std::cout << "~B" << std::endl;
};
};
int main()
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
}
智能指针的使用选择
如果希望只有一个指针管理一个资源或者管理一个数组就用unique_ptr,如果希望多个指针管理使用同一个资源就用shared_ptr,weak_ptr用来帮助解决shared_ptr引发的问题