1. 概述
Effective Modern C++中指出,在给智能指针std::shared_ptr初始化时,优先考虑使用std::make_shared,而非直接使用new。
总结有以下几点原因。
2.1 代码更简洁
看如下示例代码:
class TestObj {};
//使用make函数
auto ptr(std::make_shared<TestObj>());
//不使用make函数
std::shared_ptr<TestObj> ptr2(new TestObj());
从示例可以看出,不使用make_shared时,需要写2次类型名称TestObj。而使用make_shared代码更简洁。
引用书中原文:重复写类型和软件工程里面一个关键原则相冲突:应该避免重复代码。源代码中的重复增加了编译的时间,会导致目标代码冗余,并且通常会让代码库使用更加困难。它经常演变成不一致的代码,而代码库中的不一致常常导致bug。此外,打两次字比一次更费力,而且没人不喜欢少打字吧?
这只是一方面原因,但这不是重点,下面的问题才是根本原因。
2.2 使用new容易造成资源泄露
看如下示例代码:
class TestObj {};
int Compute() {
if (xxx) {
// 有可能抛异常
throw Exception();
}
return 0;
};
void DoSomething(std::shared_ptr<TestObj> ptr, int n) {}
DoSomething(std::shared_ptr<TestObj>(new TestObj()), Compute());
DoSomething接受2个参数,但是调用DoSomething时,编译器不能保证先构造完第1个参数,再构造第2个参数。假如编译器生成的代码按如下顺序执行:
new TestObj() Compute() std::shared_ptr (第一步new的内存)
一旦第2步Compute函数抛异常,第一步new的内存将无法释放,造成泄露。
使用std::make_shared就没有这个问题:
DoSomething(std::make_shared<TestObj>(), Compute());
一定要用new的话,将DoSomething的参数先初始化好再传入也可以:
auto ptr = std::shared_ptr<TestObj>(new TestObj());
DoSomething(ptr, Compute());
上述代码,智能指针出现了一次多余的值拷贝,如果追求极致性能,可以使用std::move:
// 使用std::move,避免值拷贝
DoSomething(std::move(ptr), Compute());
关于std::move,涉及到左值、右值等概念,下次再写。
编程之海 版权所有丨如未注明,均为原创丨转载请注明转自:https://codingsea.com/c-stdmake_shared%e7%94%a8%e9%80%94/