std::shared_ptr的使用

  • Post author:
  • Post category:其他


std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除显示的调用 delete,当引用计数变为零的时候就会将对象自动删除。


std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数, 并通过use_count()来查看一个对象的引用计数。例如:

auto pointer = std::make_shared<int>(10);
auto pointer2 = pointer; // 引用计数+1
auto pointer3 = pointer; // 引用计数+1
int *p = pointer.get(); // 这样不会增加引用计数
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3

pointer2.reset();
std::cout << "reset pointer2:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 reset
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
pointer3.reset();
std::cout << "reset pointer3:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset

创建

方式一:shared_ptr<string> pTom{new string("tom")};
方式二:shared_ptr<string> pTom;
               pTom.reset(new string("tom"));
方式三:shared_ptr<string> pTom = make_shared<string>("tom");
推荐:使用方式三,更快(一次复制),更安全

使用

shared_ptr<string> pTom = make_shared<string>("tom");
shared_ptr<string> pJerry = make_shared<string>("jerry");
(*pTom)[0] = ’T’;  //tom -> Tom
pJerry->replace(0, 1, "i"); //jerry -> Jerry

vector<shared_ptr<string>> vtWhoCleanRoom;   
vtWhoCleanRoom.push_back(pTom);   
vtWhoCleanRoom.push_back(pJerry);    
vtWhoCleanRoom.push_back(pTom);

//此时vtWhoCleanRoom的内容为Tom Jerry Tom
*pTom = "Tomy"; 
//此时vtWhoCleanRoom的内容为Tomy Jerry Tomy

比较运算符


所有比较运算符都会调用共享指针,内部封装的原始指针的比较运算符;支持==、!=、<、<=、>、>=;同类型的共享指针才能使用比较运算符。

shared_ptr<int> sp_n1 = make_shared<int>(1);
shared_ptr<int> sp_n2 = make_shared<int>(2);
shared_ptr<int> sp_nu;
shared_ptr<double> sp_d1 = 
    make_shared<double>(1);

bool bN1LtN2 = sp_n1 < sp_n2;  //true
bool bN1GtNu = sp_n1 > sp_nu;  //true
bool bNuEqNu = sp_nu == sp_nu; //true
bool bN2GtD1 = sp_d1 < sp_n2;  //编译错误

线程安全接口


①共享指针不是线程安全的

②标准库提供了共享指针的原子接口

③只并发访问指针,而不是对它们所引用的值。

std::shared_ptr<X> global; //创建空的共享指针
void foo()
{
    std::shared_ptr<X> local{new X};
    ...
    std::atomic_store(&global, local);
}
操作 结果
atomic_is_lock_free(&sp) 如果sp的原子接口是无锁的,则返回true
atomic_load(&sp) 返回sp
atomic_store(&sp,sp2) 使用sp2对sp进行赋值
atomic_exchange(&sp,sp2) 交换sp与sp2的值

错误使用


①多个共享指针不能拥有同一个对象

②可以使用enable_shared_from_this

和shared_from_this生成共享指针

#include <iostream>
#include <memory>

using namespace std;

class CTest{
public:
    CTest(const string & sName) : m_sName(sName) {}
    ~CTest() {
        cout << m_sName << " destroy" << endl; }

private:
    string m_sName;
};


int main()
{
    CTest* p = new CTest("test");
    shared_ptr<CTest> sp1(p);
    shared_ptr<CTest> sp2(p); //错误

    return 0;
}

运行输出:

test destroy
Segmentation fault      (core dumped)

#include <iostream>
#include <memory>

using namespace std;

class CTest : public enable_shared_from_this<CTest>
{
public:
    CTest(const string & sName) : m_sName(sName) {}
    ~CTest() {
        cout << m_sName << " destroy" << endl; }

    shared_ptr<CTest> getSharePtr() {
        return shared_from_this();
    }

private:
    string m_sName;
};

int main()
{
    CTest* p = new CTest("test");
    shared_ptr<CTest> sp1(p);
    shared_ptr<CTest> sp2 = p->getSharePtr(); //正确

    return 0;
}

销毁


①定义删除器sp(…, D)

②删除器可以定义为普通函数、匿名函数、函数指针等符合签名要求的可调用对象

③只有最后引用对象的sp销毁时才会销毁对象

#include <iostream>
#include <memory>

using namespace std;

void delFun(string *p)
{
    cout << "Fun delete " << *p << endl;
    delete p;
}

int main()
{
    cout << "begin" << endl;
    shared_ptr<string> pTom;
    {
        shared_ptr<string> pTom1(new string("tom"),
                                 [](string* p) {
            cout << "Lamda delete " << *p << endl;
            delete p;
        });
        pTom = pTom1;
        shared_ptr<string> pJerry(new string("jerry"), delFun);
    }
    cout << "end" << endl;

    return 0;
}


①可以为数组创建一个共享指针,但这样做是错误的。

②共享指针提供的默认删除程序,将调用delete,而不是delete []

③可以使用自定义的删除器,删除器中使用delete[]

④也可以使用default_delete作删除器, 因为它使用delete []

#include <iostream>
#include <memory>

using namespace std;

int main()
{
    std::shared_ptr<char> sp1(new char[20]); //错误但能编译通过

    std::shared_ptr<char> sp2(new char[20],
                             [](char* p) {
                                 delete[] p;
                             }
                            );

    std::shared_ptr<char> sp3(new char[20],
                              std::default_delete<char[]>());

    return 0;
}


①除了释放内存,也可以释放其他资源

②要释放其他资源,就必须使用自定义的删除器

#include <iostream>
#include <fstream>
#include <memory>
#include <cstdio>

using namespace std;

class FileDeleter
{
  public:
    FileDeleter (const string& sFileName)
     : m_sFileName(sFileName) {
    }

    void operator () (ofstream* pOfs) {
        delete pOfs;  //关闭文件
        remove(m_sFileName.c_str()); //删除文件
        cout << "Delete file -- " << m_sFileName << endl;
    }

  private:
    string m_sFileName;
};

int main()
{
    const string sFileName = "TempFile.txt";
    std::shared_ptr<ofstream>
            fp(new ofstream(sFileName),
               FileDeleter(sFileName));
    cout << "Program exit" << endl;

    return 0;
}

强制类型转换


①共享指针强制转换运算符允许将其中包裹的指针强制转换为其他类型。

②不能使用普通的强制转换运算符,因为它会导致未定义的行为。

③共享指针的强制类型转换运算符包括

static_pointer_cast、

dynamic_pointer_cast、

const_pointer_cast

#include <iostream>
#include <memory>

using namespace std;

int main()
{
    shared_ptr<void> spv(new int(1));
    shared_ptr<int> spe(static_cast<int*>(spv.get()));
    shared_ptr<int> spn = static_pointer_cast<int>(spv);

    cout << *spe << endl;
    cout << *spn << endl;

    return 0;
}

共享指针存在的问题


①循环引用

②明确想要共享但不拥有对象



版权声明:本文为lyshiba原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。