两种创建方法:第一种:std::make_shared
创建;第二种:new
方法创建。
当使用 std::make_shared
创建一个 std::shared_ptr
时,C++ 的实现会在单个内存分配中同时为对象本身和与之相关的控制块(包括引用计数)分配内存。这种内存分配策略与直接使用 std::shared_ptr
的构造函数配合 new
操作符有显著的不同,后者通常涉及两个独立的内存分配:一个用于对象本身,另一个用于控制块。下面详细解释这两种方法的不同:
使用 std::make_shared
当你调用 std::make_shared
时,如:
auto ptr = std::make_lushared<SomeClass>(args...);
C++ 标准库会进行以下操作:
单个内存分配:分配足够的内存来存储 SomeClass
的实例和相关的控制块。控制块通常包括引用计数、弱引用计数和可能的其他元数据。
构造对象:在已分配的内存中的适当位置构造 SomeClass
的实例。构造函数的参数 args...
将被传递给 SomeClass
的构造函数。
初始化控制块:设置引用计数为 1,弱引用计数根据需要进行设置。
这种方法的好处是减少了内存分配的次数(从两次减少到一次),这可以提高内存分配的效率,减少内存碎片。此外,因为对象和控制块位于相邻的内存区域,可能还会改善缓存的利用率。
使用 new 操作符与 std::shared_ptr 构造函数
当你创建一个 std::shared_ptr
通过直接使用 new
操作符时,如:
std::shared_ptr<SomeClass> ptr(new SomeClass(args...));
则涉及以下步骤:
对象内存分配:使用 new
操作符分配内存并构造 SomeClass
的实例。这涉及调用 SomeClass
的构造函数,参数为 args...
。
解释:当使用 new SomeClass(args…) 时,首先会在堆上分配足够的内存来存储 SomeClass
类型的对象。然后,使用传递的参数 args… 来构造这个对象。这里的内存分配是专门用来存储对象实例本身的数据。
控制块内存分配:当 std::shared_ptr
构造时,它将分配一个控制块来管理对象的生命周期和存储引用计数。
解释:std::shared_ptr 除了管理对象的指针外,还需要管理与对象生命周期相关的附加信息,比如引用计数(用来追踪有多少个
shared_ptr 实例共享同一个对象)和弱引用计数(用来追踪有多少个 weak_ptr
实例被关联)。为了存储这些信息,std::shared_ptr 在创建时会在堆上分配一个控制块(control
block)。这个控制块通常包括引用计数、弱引用计数和一个析构器函数指针等。
这种方法涉及两次独立的内存分配,一次是为对象本身,另一次是为控制块,这可能导致较低的内存分配效率和更多的内存碎片。
总结
因此,std::make_shared
的单一内存分配方法提供了效率和性能上的优势。然而,如之前提到的,当需要使用自定义删除器或在某些特殊情况下管理外部资源时,直接使用 new
和 std::shared_ptr
的构造函数可能更合适。