目录
1.首先复习一下智能指针
在C++11中,引入了新的智能指针类,用于更安全和方便地管理动态分配的资源,避免内存泄漏和悬空指针等问题。以下是C++11中的三种主要智能指针:
【1】std::unique_ptr
:
1,std::unique_ptr
是一种独占式智能指针,用于管理唯一的对象,确保只有一个指针可以访问该对象。不能赋值或拷贝给其他unique_ptr
,可以通过移动语义来转移所有权.通过 std::make_unique
函数可以创建 std::unique_ptr
对象(推荐这中方式创建对象),如:std::unique_ptr<int> ptr = std::make_unique<int>(42);
....
{
std::unique_ptr<int> c1 = std::make_unique<int>(100);
std::unique_ptr<int> c2 = std::make_unique<int>(200);
std::unique_ptr<int> c3(c2); //err
//如果上面错误修改成下面
std::unique_ptr<int> c3(std::move(c2)); //c2为空
c2.get() //err
}
2,使用 std::unique_ptr
可以自动释放动态分配的内存,当指针超出作用域或被重置时,它会自动删除所管理的对象。
int main()
{
std::unique_ptr<int> c1 = std::make_unique<int>(20);
{
std::unique_ptr<int> c1 = std::make_unique<int>(20);
//c1.reset(); 不影响下面的c1打印
cout << c1.get() << end; //超出作用域就会释放
}
cout << c1.get() << end;
}
3,需要注意unique_prt作函数参数时,需要注意形参还是实参。
void funtion1(std::unique_ptr<int> c1)
{
c1.get();
}
void funtion2(std::unique_ptr<int>& c1)
{
c1.get();
}
....
{
std::unique_ptr<int> val1 = make_unique<int>(1);
std::unique_ptr<int> val2 = make_unique<int>(2);
funtion1(val1);//err
funtion1(std::move(val1));
val1.get(); //err
funtion2(val2 );
}
4.unique_ptr转shared_ptr(可以unique_ptr作函数返回值,这样shared_ptr也可以用)
....
{
std::unique_ptr<int> c1 = make_unique<int>(10);
std::shared_ptr<int> c2 = std::move(c1);
c1.get(); /err
}
【2】std: :shared_ptr:
1,std::shared_ptr
是一种共享式智能指针,多个指针可以同时共享对同一对象的拥有权。
2,std::shared_ptr
使用引用计数技术追踪所管理对象的引用数量,当引用计数变为零时,自动销毁所管理的对象。
3,通过 std::make_shared
函数可以创建 std::shared_ptr
对象,如:std::shared_ptr<int> ptr = std::make_shared<int>(42);
【3】std::weak_ptr
:
1,std::weak_ptr
是一种弱引用智能指针,它可以解决 std::shared_ptr
的循环引用问题。
2,std::weak_ptr
指向 std::shared_ptr
管理的对象,但不会增加引用计数。因此,当所有 std::shared_ptr
对象超出作用域后,即使还有 std::weak_ptr
对象存在,所管理的对象也会被销毁。
3,通过 std::shared_ptr
的 std::weak_ptr
构造函数可以创建 std::weak_ptr
对象,如:std::weak_ptr<int> weakPtr = sharedPtr;
2.说下std::shared_ptr循环问题
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::shared_ptr<B> bptr;
~A() {
cout << "A is deleted" << endl; // 析构函数后,才去释放成员变量
}
};
class B {
public:
std::shared_ptr<A> aptr;
~B() {
cout << "B is deleted" << endl; // 析构函数后,才去释放成员变量
}
};
int main()
{
std::shared_ptr<A> pa;
{
std::shared_ptr<A> ap = std::make_shared<A>();
std::shared_ptr<B> bp = std::make_shared<B>();
ap->bptr = bp;
bp->aptr = ap;
}
return 0;
}
上述情况形成了闭环,没有其他指针指向它们时,就会导致循环引用问题。这种情况下,内存无法被垃圾收集器正确释放,从而可能导致内存泄漏,在这种情况下,A的实例可以通过bPtr引用B的实例,反之亦然。但是,如果这些是唯一的引用,那么垃圾收集器无法确定这两个对象何时不再需要,因为它们互相引用,形成了一个循环。
解决办法:
常见的
解决这种问题的一种常见方法是使用
std::weak_ptr
代替std::shared_ptr
。std::weak_ptr
不会增加所指向对象的引用计数,因此不会阻止其被垃圾回收。当你有需要访问一个std::weak_ptr
所指向的对象时,你可以临时将其升级为std::shared_ptr
。如果在此期间没有其他的std::shared_ptr
或std::weak_ptr
访问过这个对象,那么这个对象就会被垃圾回收。在上述例子中,可以将std::shared_ptr
替换为std::weak_ptr
另一种
另一种可能的解决方案是使用
std::atomic<bool>
来控制循环引用的对象的生命周期。你可以在对象创建时将该值设置为true,在对象不再需要时将其设置为false。然后,你可以在访问对象之前检查该值是否为true。如果为false,则说明对象已经被销毁,此时访问将导致未定义的行为。因此,这可以防止在对象被销毁后仍被使用的情况。