std::make_shared
是 C++11 引入的一个工具函数,用于创建并返回一个 std::shared_ptr
,它封装了动态内存分配和 std::shared_ptr
的构造过程。
引入 std::make_shared
的主要目的是为了提高代码的安全性、性能和可读性。
为什么要引入 std::make_shared
?
在 C++11 之前,创建 std::shared_ptr
通常需要显式地使用 new
分配内存,然后将指针传递给 std::shared_ptr
的构造函数。这种方式存在一些问题:
- 代码冗余:需要显式地使用
new
和std::shared_ptr
,代码不够简洁。 - 潜在的内存泄漏风险:如果在
new
和std::shared_ptr
构造之间发生异常,可能会导致内存泄漏。 - 性能开销:
std::shared_ptr
需要维护一个控制块(用于引用计数等),如果单独使用new
和std::shared_ptr
,会导致两次内存分配(一次是对象本身,一次是控制块)。
std::make_shared
的引入解决了这些问题。
std::make_shared
的优点
- 代码简洁性:
std::make_shared
将内存分配和std::shared_ptr
的构造合并为一个步骤,代码更简洁。
- 异常安全:
- 使用
new
的方式可能在分配内存和构造std::shared_ptr
之间发生异常,导致内存泄漏。 std::make_shared
是原子操作,避免了这种风险。
- 使用
- 性能优化:
std::make_shared
通常会一次性分配内存,既存储对象又存储控制块,减少了内存分配的次数。- 单独使用
new
和std::shared_ptr
会导致两次内存分配(对象和控制块),而std::make_shared
只需要一次。
- 减少内存碎片:
- 由于
std::make_shared
将对象和控制块分配在连续的内存区域,可以减少内存碎片。
- 由于
使用示例
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int value) : value_(value) {
std::cout << "MyClass constructed with value: " << value_ << std::endl;
}
~MyClass() {
std::cout << "MyClass destroyed with value: " << value_ << std::endl;
}
private:
int value_;
};
int main() {
// 使用 make_shared 创建 shared_ptr
auto ptr = std::make_shared<MyClass>(42);
std::cout << "Use count: " << ptr.use_count() << std::endl;
return 0;
}
输出:
MyClass constructed with value: 42
Use count: 1
MyClass destroyed with value: 42
注意事项
- 不支持自定义删除器:
std::make_shared
不支持自定义删除器。如果需要自定义删除器,必须直接使用std::shared_ptr
的构造函数。
- 对象和控制块的生命周期绑定:
std::make_shared
将对象和控制块分配在连续的内存区域,因此即使对象的引用计数归零,控制块的内存也不会立即释放,直到所有弱引用(std::weak_ptr
)也被释放。
- 不适合用于大对象:
- 如果对象非常大,且需要提前释放内存,使用
std::make_shared
可能会导致内存占用时间过长。
- 如果对象非常大,且需要提前释放内存,使用
总结
std::make_shared
的主要优点是代码简洁、异常安全和性能优化。- 它通过一次性分配内存(对象和控制块)减少了内存分配次数和内存碎片。
- 适用于大多数需要
std::shared_ptr
的场景,但在需要自定义删除器或特殊内存管理时,可能需要直接使用std::shared_ptr
的构造函数。
这个问题考察的是对 C++ 智能指针和内存管理的理解,尤其是 std::make_shared
的设计初衷和优势。
THE END
暂无评论内容