C++11智能指针(知乎分享)
Smart pointer 会不会比Raw pointer效率低?
- 想把智能指针用成瓶颈的话,基本上是不可能的,不要过度担心
Smart pointer 不用自己管理内存了,是不是对c++程序员要求低了
- 显然是要求更高了,想要用好智能指针的前提是会熟练使用裸指针,而且要了解智能指针的实现原理,要根据实际情况去选择shared_ptr及unique_ptr。智能指针使用不当同样会内存泄漏
unique_ptr使用
unique 顾名思义 ownership 唯一
1.推荐使用make_unique进行初始化(C++14)
std::unique_ptr<int> pTest = std::make_unique<int>(3);
2. 函数传递时避免使用unique_ptr作为函数参数,除非需要转移所有权到函数里面,一般通过Get函数获取裸指针去传递
void func1(std::unique_ptr<int> p){
*p = 10;
}
void func2(int* p) {
*p = 10;
}
int main() {
std::unique_ptr<int> pInt = std::make_unique<int>(3);
func2(pInt.get());
}
如果一定要使用unique_ptr进行传递,可以选择使用引用(不推荐,原因见文章末尾)
#include <memory>
void func1(std::unique_ptr<int>& p){
*p = 10;
}
int main() {
std::unique_ptr<int> pInt = std::make_unique<int>(3);
func1(pInt);
}
3.unique_ptr经常作为返回值使用,设计模式中经常用到,异常安全
如工厂模式
class PizzaFactory {
public:
enum PizzaType {
HamMushroom,
Deluxe,
Hawaiian
};
static unique_ptr<Pizza> createPizza(PizzaType pizzaType) {
switch (pizzaType) {
case HamMushroom: return make_unique<HamAndMushroomPizza>();
case Deluxe: return make_unique<DeluxePizza>();
case Hawaiian: return make_unique<HawaiianPizza>();
}
throw "invalid pizza type.";
}
};
或者Prototype原型模式
class RecordFactory
{
private:
unordered_map<RecordType, unique_ptr<Record>, hash<int> > m_records;
public:
RecordFactory()
{
m_records[CAR] = make_unique<CarRecord>("Ferrari", 5050);
m_records[BIKE] = make_unique<BikeRecord>("Yamaha", 2525);
m_records[PERSON] = make_unique<PersonRecord>("Tom", 25);
}
unique_ptr<Record> createRecord(RecordType recordType)
{
return m_records[recordType]->clone();
}
};
shared_ptr
- 我用c++开发服务器端时,因为要并发,经常用到shared_ptr
- 初始化同样使用make_shared
std::shared_ptr<int> pInt = std::make_shared<int>(3);
- 避免循环引用的坑(你中有我,我中有你。解决方法是将其中一个改成weak_ptr)
struct A;
struct B;
struct B {
std::shared_ptr<int> pA;
};
struct A {
std::shared_ptr<int> pB;
};
- 使用智能指针时,避免使用new 裸指针
- 我喜欢将shared_ptr 放到std::vector<>中 ~ ~
备注:什么时候用shared_ptr,什么时候用unique_ptr?
- 判别很简单,就是ownership,独占所有权就用unique_ptr ,共享就shared
- 想不明白的时候,我一般先会使用uniqe_ptr ,当编译器编译不通过的时候,我再换shared_ptr
- 0或者直接用shared_ptr ,后期再重构
注意事项:
参数传递时,尽量不要使用智能指针的引用
分两种情况讨论:shared_ptr与unique_ptr。shared_ptr本身就不必要传递引用,因为不会发生所有权变化。而就unique_ptr来说,传递引用会造成:
- 接受引用方对此资源是否具有所有权?如果有,则破坏了unique_ptr的独一所有权语义,应考虑move或者换成shared_ptr;如果没有,应考虑传递裸指针而非传递引用。
- 接受引用方会否将资源长期持有?比如多线程之间资源传递。若有,该资源生命周期不被引用方控制,会造成隐形bug。
总结:正确使用智能指针的方式是
- 在函数中作为参数传递是,尽量避免使用智能指针,使用*或者引用,跟以前一样
- 尽量使用unique_ptr,他比shared_ptr更light
- 尽量使用makeshared/ make_unique 来代替new
- 不出现new 和 delete