boost::make_shared没有调用(放置)操作符new
boost::make_shared is not calling (placement) operator new?
我第一次使用boost::make_shared来创建由共享指针指向的对象。主要是因为我们的代码太慢了,而单个分配确实有助于提高性能。
在修复了一些内存泄漏后,我决定通过覆盖所有相关类的新运算符来实现一个简单的内存泄漏检测器,只是为了计算在我们的应用程序中的特定点上哪些对象仍然存在。我以前已经实现过好几次了,很惊讶地发现我的代码不再检测任何对象。
我想我所要做的就是覆盖"placement new"而不是"normal"operator new,因为make_shared的boost网站文档中有以下内容:
"效果:分配适合T类型对象的内存,并且通过placementnew表达式new(pv)在其中构造一个对象T()或新的(pv)T(std::forward(args)…)。allocate_shared使用的副本来分配内存。如果抛出异常,则没有效应"
然而,我的新职位也没有被要求。我写了一个小测试程序来重现行为:
#include <iostream>
using namespace std;
#include "boost/shared_ptr.hpp"
#include "boost/make_shared.hpp"
class Test
{
public:
Test() { cout << "Test::Test()" << endl; }
void* operator new (std::size_t size) throw (std::bad_alloc) {
cout << "Test new" << endl;
return malloc(size);
}
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw() {
cout << "Test non-throwing new" << endl;
return malloc(size);
}
void* operator new (std::size_t size, void* ptr) throw() {
cout << "Test non-throwing placement new" << endl;
return malloc(size);
}
};
void* operator new (std::size_t size) throw (std::bad_alloc) {
cout << "Global new" << endl;
return malloc(size);
}
int main() {
cout << "..." << endl;
boost::shared_ptr<Test> t1(boost::make_shared<Test>());
cout << "..." << endl;
boost::shared_ptr<Test> t2(new Test());
cout << "..." << endl;
return 0;
}
它呈现以下输出:
...
Global new
Test::Test()
...
Test new
Test::Test()
Global new
...
我期待着在输出的第三行出现"新的测试非投掷位置"。你认为这种行为应该是什么?你同意根据make_shared的文档,它应该调用我的测试类的placement new操作符吗?还是我误解了?
我可以在本地复制boosts实现,当然也可以添加对placement新操作符的调用。但是,这是合适的吗,还是会违反新放置的预期语义?
提前感谢您的时间和帮助。
作为make_shared
的源,它使用全局放置new
运算符,而不是类提供的新运算符。
::new( pv ) T();
不幸的是,(至少在OS X上是这样)(根据标准),您无法定义自己的全局放置新操作符。allocate_shared
似乎更符合您的需求。
编辑:
另一种选择可能是实际编写make_shared
的一个版本,该版本使用类的新位置而不是全局位置。它只有大约10行代码,只要你遵守原始代码的许可证,应该没问题。
您不能替换新的放置(§18.4。1.3,参见例如这个问题),因此给出的输出似乎很好。
作为修改Boost标头的替代方案,您可以查看像Valgrind这样的外部工具。
为您的特定类型实现的operator new
将仅用于您的类型的元素是用new
动态分配的表达式,例如Test *p = new Test;
。现在make_shared
不是动态地分配您类型的对象,而是一个缓冲区,它为共享计数(包括计数器、deleter和一些额外的位和块)和您的对象保存足够的信息。
然后它使用placement new来调用对象的构造函数。请注意,placement new在这种情况下不是分配内存,在C++中调用已分配内存块上的构造函数只是一种有趣的语法。这实际上可能是混淆的根源,因为new
表达式、operator new
和放置新是三个碰巧共享一个名称的不同概念。
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- C++放置新和初始化
- 有没有办法让编译器在我放置字符串而不是 nlohmann::json 对象时抛出错误?
- 是什么让放置新调用对象的构造函数?
- C++ 防止在映射中放置()时调用析构函数
- 在哪里放置我的函数?进入我的母语 Gui 还是进入我的演示者?
- 放置派生C++的新基子对象
- 如何在QByteArray中放置和检索位字段而不会感到痛苦?
- 使用派生类的新放置
- 新的放置取决于 iostream
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 放置上的析构函数 - 新
- 将放置新与存储类一起使用时的额外构造
- 放置 new 和 uninitialized_fill() 的行为
- 在使用放置新操作符时,我真的需要担心对齐问题吗
- boost::make_shared没有调用(放置)操作符new
- 将构造函数参数转发给放置new操作符
- 使用放置-新建操作符和复制构造函数代替赋值操作符
- 如何删除通过放置新操作符构造的对象
- 将原始操作符new、放置new和标准删除合并是否合法