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<typename _Tp, typename... _Args>
- 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<typename _Tp, typename _Alloc, typename... _Args>
- 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<typename _Alloc, typename... _Args>
- __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<typename T, typename... Arg> // 可能有多个参数,所以用 ...
- 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;
- }
源码:std::make_unique, std::make_unique_for_overwrite - cppreference.com
很简单
- 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)