为什么'std :: make_shared'总是使用全局内存分配,即使类重载了new / delete运算符也是如此?

Pat*_*oul 4 c++ make-shared c++11

使用std::make_shared<C>该类时,不会调用重载的new / delete运算符。

使用时std::shared_ptr<C>std::unique_ptr<C>std::make_unique<C>类超载新/使用delete运算符。

查看文档时,它是完全正确的且有据可查。

cppreference解释了该行为:

std::make_shared使用:: new,因此,如果使用特定于类的运算符new设置了任何特殊行为,则它将不同于std::shared_ptr<T>(new T(args...))

下面是一些伪代码,可以更好地突出显示行为:

#include <memory>

class C {
 public:
  void* operator new(size_t size) {
    void* p = ::operator new(size);
    std::cout << "C::new() -> " << p << "\n";
    return p;
  }

  void operator delete(void* p) {
    std::cout << "C::delete() -> " << p << "\n";
    ::operator delete(p);
  }
};

std::shared_ptr<C> ptr = std::make_shared<C>();
Run Code Online (Sandbox Code Playgroud)

从外部的角度来看,它似乎不一致且容易出错。应该始终使用重载类new / delete运算符。

那么,这种行为的原理是什么?

而且,C ++规范在哪里详细说明了std::make_shared行为?

谢谢你的帮助。

Nat*_*ica 9

那么,行为的合理性是什么?

这样做的原因是因为make_shared不仅分配了对象,还分配了的控制块shared_ptr。为了使其尽可能高效,它调用new一次并一次性为控制块和对象分配足够的存储空间。否则,它将不得不调用new两次,这会使分配开销增加一倍。

如果您想使用自定义分配器,则需要使用std::allocate_shared,它将使用您的自定义分配器进行一次内存获取来创建shared_ptr


另一种选择是使用std::make_unique来创建unique_ptr,然后使用来初始化shared_ptr。之所以有效,是因为unique_ptr没有控制块,所以std::make_unique

unique_ptr<T>(new T(std::forward<Args>(args)...))
Run Code Online (Sandbox Code Playgroud)

那会给你

std::shared_ptr<C> ptr = std::make_unique<C>();
Run Code Online (Sandbox Code Playgroud)

哪个输出

C::new() -> 0xf34c20
C::delete() -> 0xf34c20
Run Code Online (Sandbox Code Playgroud)

  • 有时C++只会让你痛苦地呻吟。 (2认同)
  • @einpoklum在大多数情况下是无痛的。这只是不为您的类型超载new和delete的另一个原因。如果他们真的愿意,可以使用“ unique_ptr”(一分钟内将其添加到答案中)。 (2认同)

Ser*_*eyA 5

此行为的直接原因是std::make_shared执行单个分配以同时分配控制块和对象的事实。它没有其他选择,只能为此使用全局运算符new。

另外,我想提到的是,我个人认为对于C ++最不理想的功能之一,可以重载new / delete运算符。类不应规定其内存分配方法。而是,应该将类分配委派给手头的特定任务,并且设计良好的类应该表现得一样好,无论是在动态存储,自动存储,内存映射文件还是闪存卡中进行分配。