std::make_unique
用于创建
实例std::unique_ptr
std::make_shared
用于创建std::shared_ptr
实例
目录
1. make_unique
std::make_unique
是一个 C++14 中引入的函数模板,用于创建 std::unique_ptr
实例。它的目的是简化创建动态分配对象的过程,并且提供了更好的异常安全性。在使用 std::make_unique
时,你只需提供要动态分配的对象的类型和构造函数参数,函数会返回一个包装了这个动态分配对象的 std::unique_ptr
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "构造函数" << endl;
}
Person(int, int)
{
cout << "构造函数" << endl;
}
~Person()
{
cout << "析构函数" << endl;
}
};
void test()
{
// 使用默认构造
unique_ptr<Person> up1 = make_unique<Person>();
// 使用有参构造
unique_ptr<Person> up2 = make_unique<Person>(10, 20);
// 创建对象数组
unique_ptr<Person[]> up3 = make_unique<Person[]>(3);
}
int main()
{
test();
return 0;
}
由于 make_unique 函数调用对象的默认构造函数创建对象数组。所以,类内需要提供默认构造函数,否则无法使用
make_unique 函数和 unique_ptr 构造函数创建智能对象时的区别是什么呢?
1. 从语法角度:make_unique 函数比 unique_ptr 语法更加简洁
2. 从安全角度:make_unique 是异常安全的,而 unique_ptr 构造函数创建方式则不是。
异常安全性是指当函数在执行过程中发生异常时,程序依然能够保持数据结构的一致性和资源的正确释放。对于 make_unique
,它保证了在异常发生时内存会被正确释放,即使在内存分配后抛出异常也不会导致内存泄
#include <iostream>
#include <memory>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 构造函数" << endl;
}
~Person()
{
cout << "Person 析构函数" << endl;
}
};
void do_logic(unique_ptr<Person> uq, int number) {}
int get_number()
{
cout << "get_number" << endl;
throw exception();
return 100;
}
int main()
{
try
{
// 1. 构造函数 异常不安全
do_logic(unique_ptr<Person>(new Person), get_number());
// 2. make_unique 异常安全
// do_logic(make_unique<Person>(), get_number());
}
catch (...)
{
cout << "错误处理..." << endl;
}
return 0;
}
结果发现析构函数没有执行,就会造成内存泄漏,不会构造,在windows和gcc执行的结果不同,就是说在不同的编译器,代码执行的顺序是不一样的,先去执行get_number函数,不会进行new的操作。
2.make_shared
make_shared
是 C++ 标准库提供的一个函数模板,用于创建并返回一个 std::shared_ptr
,它与 new
操作符相比具有更好的性能和异常安全
class Person
{
public:
Person(int, int)
{
cout << "无参构造函数" << endl;
}
~Person()
{
cout << "析构函数" << endl;
}
};
void test()
{
shared_ptr<Person> sp = make_shared<Person>(10, 20);
}
注意:make_shared 函数并不支持创建用于管理动态对象数组的 shared_ptr 对象,这个和 make_unique 是有区别的
当创建 sp 对象时,过程如下:
- 首先,在堆上创建 Person 动态对象,需要分配一次内存。
- 然后,在堆上创建引用计数对象,需要分配一次内存。
- 最后,在栈上创建 shared_ptr 对象。
从上面过程来看,很显然,我们需要为 Person 动态对象、引用计数对象动态分配两次内存
而当使用 make_shared 函数时:
- 一次性分配 Person 动态对象和引用计数对象所需要的内存,并在该内存中构建 Person 对象和引用计数对象。
- 然后,在栈上创建 shared_ptr 对象。
从该过程中,我们发现 make_shared 函数会比 shared_ptr 构造函数创建方式减少一次动态内存的申请和释放,进而提升了 shared_ptr 的构建效率