C++ 智能指针使用

推荐:cplusplus

维护并共享对象的所有权,这个是shared_ptr 指针的特点。

一:shared_ptr 创建使用

常用的构造:

template <class U> explicit shared_ptr (U* p);
template <class U, class D> shared_ptr (U* p, D del);

del 是自定义不抛异常的deleter 回调函数。

这里shared_ptr 指针构造函数的样例:shared_ptr construct

以下是补充说明:

//for smart pointer
#include <iostream>
#include <memory>
using namespace std;
struct Test {
    int m;
    Test(int n=0):m(n){cout<<this<<" ,default construct\n";}
    Test(const Test& obj):m(obj.m){cout<<this<<" ,copy construct\n";}
    Test(Test &&obj):m(obj.m){cout<<this<<" ,move construct\n";}
    ~Test(){cout<<this<<" ,disconstruct\n";}
    void showValue() {
        cout<<"data in object:"<<m<<endl;
    }
    
    static void deleter(Test *p) noexcept {
        cout<<"custom deleter,to delete: "<<p<<endl;
        delete p;
    }
};
void test_sharedptr(){
    #if 0
    {
        Test obj;
        //shared_ptr<Test> testobj1 (&obj,[&](Test *){});
        shared_ptr<Test> testobj1 (&obj);
        shared_ptr<Test> testobj2 = testobj1;
        cout<<"testobj1 usecout:"<<testobj1.use_count()<<"\n"; 
        cout<<" here is double free about local variable ,obj"<<endl;
    }
    #endif
    {
        cout<<"\nexample 2,heap obj :\n";
        Test* obj2 =new Test;
        shared_ptr<Test> testobj3 (obj2);
        shared_ptr<Test> testobj4 = testobj3;
        cout<<"testobj3 usecout:"<<testobj3.use_count()<<"\n";
    }
    
    {
        cout<<"\nexample 3,set deleter example:"<<endl;
        shared_ptr<Test> testobj5(new Test,Test::deleter);
        shared_ptr<Test> testobj6 = testobj5;
        cout<<"testobj5 usecout:"<<testobj5.use_count()<<"\n";
    }
    {
        cout<<"\nexample 4,by make_shared:"<<endl;
        shared_ptr<Test> testobj7 = make_shared<Test>(10);
        testobj7->showValue();
    }
}
int main()
{
    test_sharedptr();
    return 0;
}

结果:

example 2,heap obj :
0x55b5622c82c0 ,default construct
testobj3 usecout:2
0x55b5622c82c0 ,disconstruct

example 3,set deleter example:
0x55b5622c82e0 ,default construct
testobj5 usecout:2
custom deleter,to delete: 0x55b5622c82e0
0x55b5622c82e0 ,disconstruct

example 4,by make_shared:
0x55b5622c82f0 ,default construct
data in object:10
0x55b5622c82f0 ,disconstruct

要注意的点:

1:shared_ptr 管理的obj对象,必须不能是临时的变量,即,不可以是栈上的对象,否则会有double free的问题。

2:构造的原始指针,不能构造两个sp对象

int main() {
    int * n= new int(10);
    std::shared_ptr<int> test1 (n);
    std::shared_ptr<int> test2 (n);
    std::cout<<test1.use_count()<<" , cout :"<<test2.use_count()<<std::endl;

    return 0;
}

double free.报错:

1 , cout :1
free(): double free detected in tcache 2
Aborted

3:enable_shared_from_this 

struct Test {
    int m;
    /*...skip...*/
    shared_ptr<Test> getSharedObj() {
        return shared_ptr<Test>(this);
    }
};

{
        cout<<"\nexample 5,shared object:"<<endl;
        shared_ptr<Test> testobj8 = make_shared<Test>(10);
        shared_ptr<Test> testobj9 = testobj8->getSharedObj();
}

double free报错:

example 5,shared object:
0x55f61ea692f0 ,default construct
0x55f61ea692f0 ,disconstruct
double free or corruption (out)

我们改进下类的定义:

struct Test:public enable_shared_from_this<Test> {
    int m;
    Test(int n=0):m(n){cout<<this<<" ,default construct\n";}
    Test(const Test& obj):m(obj.m){cout<<this<<" ,copy construct\n";}
    Test(Test &&obj):m(obj.m){cout<<this<<" ,move construct\n";}
    ~Test(){cout<<this<<" ,disconstruct\n";}
    void showValue() {
        cout<<"data in object:"<<m<<endl;
    }
    
    static void deleter(Test *p) noexcept {
        cout<<"custom deleter,to delete: "<<p<<endl;
        delete p;
    }
    shared_ptr<Test> getSharedObj() {
       // return shared_ptr<Test>(this);
       return shared_from_this();
    }
};

如果还有其他的点,请添加到评论,感谢。

二,weak_ptr

std::weak_ptr是C++11中引入的一种智能指针,它可以用来协助std::shared_ptr。weak_ptr的主要特征和使用场景如下:

  1. weak_ptr不控制对象的生命周期,它不会影响对象的引用计数。
  2. weak_ptr指向一个由shared_ptr管理的对象,可以通过weak_ptr的lock()函数获取共享对象的shared_ptr。
  3. 当被管理的对象销毁后,weak_ptr不会自动置空,需要检查lock()的返回值来判断对象是否已销毁。
  4. weak_ptr主要用来解决shared_ptr循环引用问题,避免内存泄漏。
  5. weak_ptr可以在类中保留对外部shared对象的引用,而不会延长对象生命周期。
std::shared_ptr<Object> obj = std::make_shared<Object>();
std::weak_ptr<Object> weakObj(obj); 

// 使用obj

if(std::shared_ptr<Object> obj2 = weakObj.lock()) {
  // 使用obj2
} else {
  // obj已销毁
}

上面代码中,weakObj不会延长obj的生命周期。通过lock检查obj是否已销毁后再使用。

如果要引用weak_ptr管理的对象指针,则需要通过lock 转换成share_ptr指针。

所以weak_ptr适合用来在不延长生命周期的情况下获取共享对象的指针

struct TestWeak2;
struct TestWeak3;
struct TestWeak1 {
    shared_ptr<TestWeak2> mptr;
    shared_ptr<TestWeak3> mptr3;
    ~TestWeak1(){cout<<__func__<<" TestWeak1 disconstruct\n"; }
};
struct TestWeak2{
    shared_ptr<TestWeak1> mptr;
    ~TestWeak2(){cout<<__func__<<" TestWeak2 disconstruct\n"; }
};
struct TestWeak3{
    weak_ptr<TestWeak1> mptr;
    ~TestWeak3(){cout<<__func__<<" TestWeak3 disconstruct\n"; }
};
void test_weakptr() {
    {
       shared_ptr<TestWeak1> testobj1= make_shared<TestWeak1>();
       shared_ptr<TestWeak2> testobj2= make_shared<TestWeak2>();
       testobj1->mptr = testobj2;
       testobj2->mptr = testobj1;
       cout<<"testobj1 cout: "<<testobj1.use_count()<<", testobj2 cout: "<<testobj2.use_count()<<endl;
   }
   cout<<"\nthe following test wake_ptr:"<<endl;
   {
       shared_ptr<TestWeak1> testobj1= make_shared<TestWeak1>();
       shared_ptr<TestWeak3> testobj3= make_shared<TestWeak3>();
       testobj1->mptr3 = testobj3;
       testobj3->mptr = testobj1;
       cout<<"testobj1 cout: "<<testobj1.use_count()<<", testobj3 cout: "<<testobj3.use_count()<<endl;
   }
   cout<<"exit test_weakptr"<<endl;
}

结果:

testobj1 cout: 2, testobj2 cout: 2

the following test wake_ptr:
testobj1 cout: 1, testobj3 cout: 2
~TestWeak1 TestWeak1 disconstruct
~TestWeak3 TestWeak3 disconstruct
exit test_weakptr

以上例子,列举了shared_ptr相互引用,导致在生命周期结束的时候,引用计数不为0,对象也不会被销毁,会引起内存泄漏。引入了weak_ptr, 即打破死循环。

三, unique_ptr

不共享所有权的智能指针。同一个时刻,一个object 只能被一个unique_ptr管理。可以借move移动操作,在不同的unique_ptr对象之间移动,也有另外一种称呼,叫转移所有权。

主要的构造形式:

explicit unique_ptr (pointer p) noexcept;
unique_ptr (unique_ptr&& x) noexcept;

所有的类型可查相关API: cplusplus

没有拷贝构造:

unique_ptr( const unique_ptr& ) = delete;

另外,不同shared_ptr, C++14之前的标准库里没有maked_unique. 据说是大佬忘记了,所以在C++14 的标准里加了这个:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );

template<class T, class... Args>
std::enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>>
make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

不能创建管理数组对象的unique_ptr.

同样的,shared_ptr ,对于c++11 里,make_shared 也是不能创建数组的。

//(since C++11) (T is not array)
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

//(since C++20) (T is U[])
template< class T >
shared_ptr<T> make_shared( std::size_t N );

//(since C++20) (T is U[])
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u );

从官网上看,c++20 之后,提供了对数组的重载。

回归话题。

示例:

#include <iostream>
#include <memory>

class Foo {
public:
  Foo() { std::cout << "Foo constructed" << std::endl; }
  ~Foo() { std::cout << "Foo destroyed" << std::endl; }
};

int main() {

  // 创建一个Foo对象的unique_ptr
  std::unique_ptr<Foo> ptr1(new Foo());

  // 通过移动语义传递所有权
  std::unique_ptr<Foo> ptr2 = std::move(ptr1);

  // ptr2现在管理这个对象
  // ptr1已置空

  // ptr2离开作用域会自动释放对象
  return 0;
}

智能指针的引入吗,最大的目的是防止内存泄漏。

对于unique_ptr这么特殊的指针,使用的场景,相对就很少。什么时候使用呢?适合使用unique_ptr:

1: 临时看管某个对象

2:重要的数据,就需要独占式。

3:具有一定的线程安全特性。

      如果是线程资源,那么unique_ptr,独占式的特性,能保证绝对的线程安全。即使传给其他线程,只能通过move,移动语义来操作。

智能指针用起来,相对比较简单,接口也不多。

总结下注意点:

1:智能指针计数上是线程安全的,但是管理的对象不是线程安全的。所以,智能指针非线程安全。

2:智能指针管理的对象需要基于堆上创建。

3:shared_ptr 指针管理的类对象,类建议继承enable_shared_from_this 。

      也是防止出现double free。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值