使用中需要注意的问题:
1.循环引用导致资源没有释放
class Family {
public:
Family(std::string name) : name_{name} {}
Family(std::shared_ptr<Family> f, std::shared_ptr<Family> m, std::string name)
: name_{name}, father_{f}, mother_{m} {}
~Family() { std::cout << "destroy:" << name_ << std::endl; }
/// 会造成shared_ptr 循环引用,导致程序不析构
std::shared_ptr<Family> father_;
std::shared_ptr<Family> mother_;
// std::weak_ptr<Family> father_;
// std::weak_ptr<Family> mother_;
std::vector<std::shared_ptr<Family>> kids_;
std::string name_;
};
void test_shared_ptr(void) {
std::shared_ptr<Family> kate;
std::shared_ptr<Family> lily;
std::shared_ptr<Family> dad;
std::shared_ptr<Family> mom;
dad = std::make_shared<Family>("father");
mom = std::make_shared<Family>("mother");
kate = std::make_shared<Family>(dad, mom, "kate");
lily = std::make_shared<Family>(dad, mom, "lily");
dad->kids_.push_back(kate);
dad->kids_.push_back(lily);
mom->kids_.push_back(kate);
mom->kids_.push_back(lily);
std::cout << "dad use count:" << kate->father_.use_count() << std::endl;
std::cout << "mom use count:" << kate->mother_.use_count() << std::endl;
std::cout << "kate use count:" << kate.use_count() << std::endl;
std::cout << "lily use count:" << lily.use_count() << std::endl;
}
int main(void) {
std::cout << "hello world!" << std::endl;
test_shared_ptr();
return 0;
}
hello world!
dad use count:3
mom use count:3
kate use count:3
lily use count:3
以上代码如果father_和mother_用shared_ptr声明,就会导致循环引用,dad被引用了3次(dad本身,kate->father_和lily->father_),mom同理,kate也被引用了3三次(kate本身,dad->kids和mom->kids),当程序退出时,dad引用计数减1,mom引用计数减一,kate引用计数减一,lily引用计数减一,导致资源不释放。解决方法:father_和mother_用weak_ptr声明,这样dad被引用1次(dad本身),mom同理,kate和lily被引用了3三次(kate本身,dad->kids和mom->kids)当程序退出时,dad和mom析构,kids资源释放,kate和lily引用计数规0,资源得到释放。
2.俩个不相关的shared_ptr管理同一资源造成资源重复释放
class Value {
public:
Value(int a) : a_{a} { std::cout << "costruct" << std::endl; }
~Value() { std::cout << "distruct" << std::endl; }
public:
int a_;
};
void test_shared_ptr(void) {
Value *value = new Value{5};
std::shared_ptr<Value> value_p1 = std::shared_ptr<Value>{value};
// std::shared_ptr<Value> value_p2 = std::shared_ptr<Value>{value};
std::shared_ptr<Value> value_p2 = std::shared_ptr<Value>{
value, [](Value *) { std::cout << "do nothing" << std::endl; }};
std::cout << "value:" << value_p1->a_ << std::endl;
std::cout << "value:" << value_p2->a_ << std::endl;
}
int main(void) {
std::cout << "hello world!" << std::endl;
test_shared_ptr();
return 0;
}
hello world!
costruct
value:5
value:5
do nothing
distruct
如果把屏蔽行加上可以看到Value的析构函数被调用了俩次,虽然在实际项目中不会范这么明显的错误。但是有时候项目很复杂,比如上游给了一个cv::Mat,业务方有可能把Mat里的data封装成shared_ptr使用,导致资源重复释放,这时候就要定义自己的deleter()。
3.shared_ptr管理this指针导致资源重复释放
// class Family {
// public:
// Family(std::string name) : name_{name} {}
// Family(std::shared_ptr<Family> f, std::shared_ptr<Family> m, std::string
// name)
// : name_{name}, father_{f}, mother_{m} {}
// ~Family() { std::cout << "destroy:" << name_ << std::endl; }
// std::shared_ptr<Family> get_ptr() { return std::shared_ptr<Family>{this}; }
// /// 会造成shared_ptr 循环引用,导致程序不析构
// // std::shared_ptr<Family> father_;
// // std::shared_ptr<Family> mother_;
// std::weak_ptr<Family> father_;
// std::weak_ptr<Family> mother_;
// std::vector<std::shared_ptr<Family>> kids_;
// std::string name_;
// };
class Family : public std::enable_shared_from_this<Family> {
public:
Family(std::string name) : name_{name} {}
Family(std::shared_ptr<Family> f, std::shared_ptr<Family> m, std::string name)
: name_{name}, father_{f}, mother_{m} {}
~Family() { std::cout << "destroy:" << name_ << std::endl; }
std::shared_ptr<Family> get_ptr() { return shared_from_this(); }
/// 会造成shared_ptr 循环引用,导致程序不析构
// std::shared_ptr<Family> father_;
// std::shared_ptr<Family> mother_;
std::weak_ptr<Family> father_;
std::weak_ptr<Family> mother_;
std::vector<std::shared_ptr<Family>> kids_;
std::string name_;
};
void test_shared_ptr(void) {
std::shared_ptr<Family> kate;
std::shared_ptr<Family> lily;
// Family *dad = new Family{"father"};
std::shared_ptr<Family> dad = std::make_shared<Family>("father");
std::shared_ptr<Family> mom;
mom = std::make_shared<Family>("mother");
kate = std::make_shared<Family>(dad->get_ptr(), mom->get_ptr(), "kate");
lily = std::make_shared<Family>(dad->get_ptr(), mom->get_ptr(), "lily");
dad->kids_.push_back(kate->get_ptr());
dad->kids_.push_back(lily->get_ptr());
mom->kids_.push_back(kate->get_ptr());
mom->kids_.push_back(lily->get_ptr());
std::cout << "dad use count:" << kate->father_.use_count() << std::endl;
std::cout << "mom use count:" << kate->mother_.use_count() << std::endl;
std::cout << "kate use count:" << kate.use_count() << std::endl;
std::cout << "lily use count:" << lily.use_count() << std::endl;
}
hello world!
dad use count:1
mom use count:1
kate use count:3
lily use count:3
destroy:mother
destroy:father
destroy:lily
destroy:kate
- 由于this本质上也是一个指针,如果用return std::shared_ptr<Family>{this}的方式实际上是用俩个不相干shared_ptr管理同一个指针,会导致资源重复释放,应该以public的方式继承std::enable_shared_from_this。
- 可以使用shared_from_this()返回一个shared_ptr,
shared_ptr<_Tp> shared_from_this() {return shared_ptr<_Tp>(this->_M_weak_this);} mutable weak_ptr<_Tp> _M_weak_this;
,由于返回的是一个weak_ptr,这就使得Family *dad = new Family{"father"};
不能使用,必须要用std::shared_ptr<Family> dad = std::make_shared<Family>("father");
,shared_from_this()不会增加引用计数。
4.使用make_shared
- 提高性能
shared_ptr本身的引用计数器是一块内存,管理的资源也是一块内存,如果用make_shared,shared_ptr会申请一块大内存,而不用分别申请。 - 安全
当一个构造函数有两个参数,一个是share_ptr,另一个是其他的,如果以processWidget(std::shared_ptr<Widget>(new Widget), computePriority());
,这样的方式进行构造,会存在潜在的资源泄露,原因是当new Widget,shared_ptr构造,computePriority()的执行顺序并不确定,如果当new Widget执行完,在shared_ptr构造完成之前(还没有对new出的资源进行管理),computePriority()抛出异常导致程序终止,就会导致new出的Widge发生泄露。如果用processWidget(std::make_shared<Widget>(), computePriority());
就不会出现这问题,因为make_shared只会new一块内存,share_ptr会管理这块内存。
缺点:由于引用计数和管理的资源使用同一块内存,如果有weak_ptr存在,即使shared_ptr的引用计数为0,也不能立即释放资源(weak_ptr共享shared_ptr的引用计数,不单独申请资源),必须要等所有的weak_ptr生命周期结束后资源才能得到释放,导致资源的生命周期延长。
5.shared_ptr的线程安全
shared_ptr引用计数是线程安全的,但是资源的管理不是,需要加锁
6.用unique_ptr替代shared_ptr
在很多时候如果不需要共享,那用unique_ptr就足够了,unique_ptr没有引用计数,性能更高效。如果要转成shared_ptr也很方便,shared_ptr不能转成unique_ptr。