《Effective Modern C++》条款19:shared_ptr相关问题

本章节主要关注于shared_ptr的一些问题;

基础问题:

首先注意一下shared_ptr和unique_ptr的区别和异同;

前者对于相同变量的会存有引用计数,允许多个shared_ptr指向同一个对象;

但是需要注意的是,同一个对象指的是某个具体的实例化对象;

和unique_ptr类似,shared_ptr也可以使用自定义析构器;

class widget {
public:
    ~widget() {
        cout << "widget delete..." << endl;
    }
};


auto slef_widget_delete = [](widget* pw) {
    cout << "selfmake delete..." << endl;
    delete pw;
};

int main() {
    {
        //test filed;
        shared_ptr<widget>spw(new widget, slef_widget_delete);
    }

    return 0;
}

如下所示,值得注意的是,shared_ptr并不需要在模板实例化中告知是什么类型,这样就导致,同一类,针对于不同的实例化对象,可以定义不同的析构器,并且进行赋值移动;

换句话说,他们的类型也是相同的,不相同的只是对象堆内存中的数据结构,也就是控制块;

auto delete1 = [](widget* pw) {
    cout << "selfmake 1 delete" << endl;
    delete pw;
};

auto delete2 = [](widget* pw) {
    cout << "selfmake 2 delete" << endl;
    delete pw;
};

int main() {
    shared_ptr<widget> pw1(new widget, delete1);
    shared_ptr<widget> pw2(new widget, delete2);
    vector<shared_ptr<widget>> vpw{ pw1,pw2 };
    return 0;
}

底层实现问题:

造成shared_ptr最根源的问题在于底层实现;

和unique_ptr不同,shared_ptr的尺寸是裸指针的两倍,固定不变,不取决于有无自定义析构器;

在这里插入图片描述
从上述可以看出,针对于引用计数以及分配器,析构函数等,专门的放在了一个名叫控制块的地方;

该内存使用动态分配,并且计数等操作也依赖原子操作;

但是最关键的一点是,由于每个对象,都会拥有一个该控制块,并且所有shared_ptr共享,但是如果操作不当,可能会出现多个指向同一对象的shared_ptr拥有不同的控制块;

以下几种行为都会创建一个控制块:

  1. 使用make_shared会创建一个控制块;
  2. 从具有专属所有权的指针构造一个shared_ptr会构造一个控制块;
  3. 直接使用裸指针传入shared_ptr构造函数,会直接构造一个控制块;

因此,参照上述几条原则,最主要的原则为:

不要随随便便使用裸指针进行shared_ptr构造,应该使用工厂函数+new形式进行传参,后续如果需要共享,采用shared_ptr的复制函数进行构造;

对于本章后续,其实给出了一两个例子来论证该条的合理性;

考虑如下例子:

vector<shared_ptr<widget>> widget_list;

class widget {
public:
    ~widget() {
        cout << "widget delete..." << endl;
    }
    void process() {
    	//do something....
        widget_list.emplace_back(this);
    }
};

process很符合逻辑,针对每个处理后的widget,之后加入到队列中;

但是这里还会有问题,一是违反了传递裸指针的规则,直接传了个*this进去,二是不能保证是否该实例化类是否在别的函数中已经有了shared_ptr指向;

因此可以使用一个专门的方式;

对于shared_ptr,给了一个类似于工厂函数,来返回标准的指向该类的shared_ptr;

class widget:public enable_shared_from_this<widget> {
public:
    ~widget() {
        cout << "widget delete..." << endl;
    }
    void process() {
        widget_list.emplace_back(this);
    }
};

通过继承enable_shared_from_this<T>模板,可以解决该问题;

该种继承方式称之为“奇妙递归模板模式”;

该模板定义了一个方式:

shared_from_this();

可以直接不构造控制块,返回一个指向该实例的shared_ptr;

因此避开了重复创建控制块的问题;

但是对于第一次创建控制块,则需要使用工厂函数模式:

vector<shared_ptr<widget>> widget_list;

class widget:public enable_shared_from_this<widget> {
public:
    template<typename... Ts>
    static shared_ptr<widget> create(Ts&&... params);
    ~widget() {
        cout << "widget delete..." << endl;
    }
    void process() {
        widget_list.emplace_back(this);
    }
private:
	//构造函数
};

使用该方式,即可使用create获得初始的shared_ptr,又可以使用shared_from_this()来进行不创建块的shared_ptr创建;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值