c++ 11 中 引入了智能指针 shared_ptr,以及一个模板函数 make_shared 来生成一个制定类型的 shared_ptr。
make_shared的引入,主要有两点的提升:性能 与 异常安全
C++11 make_shared - 简书 (jianshu.com)
有如下两种方式生成 shared_ptr
int main()
{
// 1.
shared_ptr p1(new string("66888"));
cout << *p1 << endl;
// 2.
shared_ptr p2 = make_shared("888888");
cout << *p2 << endl;
return 0;
}
使用 make_shared 性能要更好一些
shared_ptr
p1(new string("66888")); 会分配两次内存: 每个std::shared_ptr都指向一个控制块,控制块包含被指向对象的引用计数以及其他东西。这个控制块的内存是在std::shared_ptr的构造函数中分配的。因此直接使用new,需要一块内存分配给string,还要一块内存分配给控制块。
使用 shared_ptr
p2 = make_shared ("888888"); 分配一次内存, 一次分配就足够了。这是因为std::make_shared申请一个单独的内存块来同时存放 string 对象和控制块。
// d.h
#include
class D
{
public:
D()
{
std::cout << "constructor D " << std::endl;
}
~D()
{
std::cout << "destructor D " << std::endl;
}
};
// main.cpp
int getVal()
{
throw 888;
return 66;
}
void init(shared_ptr ptr, int val)
{
}
int main()
{
// 1.
init(std::shared_ptr(new D), getVal());
// 2.
init(std::make_shared(), getVal());
return 0;
}
第 1 种的调用方式分为三步:
1. new D
2. 调用 shared_ptr 类的构造函数
3. 调用 getVal 函数
针对不同的编译器,上述三步的执行顺序可能不同,若是 其中的第 2 步 与第 3 步发生了调换,那么在执行 getVal 抛出异常后,就不会再执行 第 2 步,没有调用 shared_ptr 的构造函数,那么就无法用 shared_ptr 来管理 第 1 步分配出的内存。
第 2 种方式,用 make_shared 就不会出现该问题,其分为两步
1. make_shared 生成 shared_ptr 指针
2.调用 getVal 函数
上面的 1 步 与 2 步,即便发生顺序调换,也不会出现内存无法管理的情况
template
inline shared_ptr<_Tp>
make_shared(_Args&&... __args)
{
typedef typename std::remove_const<_Tp>::type _Tp_nc; // 去除 _Tp 的 const 特性,获取到其类本身
return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
std::forward<_Args>(__args)...);
// std::allocator会给我们分配一块_Tp_nc实例需要的内存空间
// 完美转发(perfect forward)剩余构造函数的参数
}
template
inline shared_ptr<_Tp>
allocate_shared(const _Alloc& __a, _Args&&... __args)
{
return shared_ptr<_Tp>(_Sp_make_shared_tag(), __a,
std::forward<_Args>(__args)...); // 调用 shared_ptr 的 private 构造函数
}
make_shared 的参数是万能引用 && ,因此参数既可以接受左值也可以接受右值。
allocate_shared 是 shared_ptr 类的 friend 函数,因此可以调用 shared_ptr 的 private 构造函数。
总之,下面的 private 构造函数将实例内存与计数器模块的内存绑定在了一起。
template
__shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
详细解析见:从零开始写一个shared_ptr-make_shared源代码解析 - 知乎 (zhihu.com)
// person.h
class Person
{
public:
Person(std::string name);
Person(const Person& p);
~Person();
std::string& getName();
void setName(std::string& name);
private:
std::string m_name;
};
// person.cpp
#include "person.h"
#include
Person::Person(std::string name):m_name(name)
{
std::cout << "Person constructor name: " << m_name << std::endl;
}
Person::Person(const Person& p)
{
this->m_name = p.m_name;
std::cout << "Person copy constructor name: " << this->m_name << std::endl;
}
Person::~Person()
{
std::cout << "Person destructor name: " << m_name << std::endl;
}
std::string& Person::getName()
{
return m_name;
}
void Person::setName(std::string& name)
{
this->m_name = name;
}
// main.cpp
void testSharedPtr2()
{
// 1.
shared_ptr ptr(new Person("Tom"));
cout << ptr->getName() << endl;
// 2.
shared_ptr ptr2 = make_shared("Jerry");
cout << ptr2->getName() << endl;
}
int main()
{
testSharedPtr2();
return 0;
}
make_unique 是 c++14 加入标准库的,用于生成独占型指针 std::unique_ptr
用 make_unique 生成独占型指针代码量更少,符合现代 c++ 尽量避免使用 new 来构造的原则
template // 可能有多个参数,所以用 ...
unique_ptr
my_make_unique(Arg&& ... s) // 支持左值与右值,所以要用万能引用
{
return unique_ptr(new T(std::forward(s)...));
}
int main()
{
unique_ptr ptr = my_make_unique("abcdd");
cout << *ptr << endl;
return 0;
}
很简单
int main()
{
std::unique_ptr a = std::make_unique("6666");
return 0;
}
参考:C++11 make_shared - 简书 (jianshu.com)
从零开始写一个shared_ptr-make_shared源代码解析 - 知乎 (zhihu.com)
《Effective Modern C++》学习笔记之条款二十一:优先选用std::make_unique和std::make_shared,而非直接new - 知乎 (zhihu.com)