我第一次使用boost :: make_shared来创建共享指针指向的对象。主要是因为我们的代码太慢而且单一分配确实有助于提高性能。
在修复了一些内存泄漏“硬手动方式”后,我决定通过覆盖所有相关类的新操作符来实现一个简单的内存泄漏检测器,只是为了计算在我们的应用程序中的特定点仍然存活的对象。我之前已经多次实现过,并且惊讶地发现我的代码不再检测到任何对象。
我认为我所要做的就是覆盖“placement new”而不是“normal”运算符new,因为make_shared的boost网站文档中有以下内容:
“效果:分配适合T 类型的对象的内存 通过放置新表达式new(pv)在其中构造一个对象 T()或新(pv)T(std :: forward(args)...) 。 allocate_shared 使用a的副本来分配内存。如果抛出异常,则没有 效应“。
我的展示位置也未被调用。我写了一个小测试程序来重现这个行为:
#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
...
我期待第3行输出的“测试非投掷位置新”。您认为该行为应该是什么?你是否同意根据make_shared的文档,它应该调用我的Test类的placement new运算符?还是我误解了?
我可以在本地复制boost实现,当然也可以添加对placement new运算符的调用。但是,这是否合适,还是会违反新布局的预期语义?
提前感谢您的时间和帮助。
答案 0 :(得分:8)
作为make_shared
的来源,它使用全局展示位置new
运算符,而不是您的类提供的新运算符。
::new( pv ) T();
不幸的是(至少在OS X上)(according to the standard),您无法定义自己的全局展示位置新运算符。似乎allocate_shared
更符合您的要求。
修改强>:
另一种方法是实际编写make_shared
版本,该版本使用类的位置new而不是全局位置。它只有大约10行代码,只要你尊重the license of the original code就应该没问题。
答案 1 :(得分:4)
您不能替换新的展示位置(§18.4.1.3,请参见例如this question),因此给出的输出似乎没问题。
作为修改Boost标头的替代方法,您可以查看Valgrind等外部工具。
答案 2 :(得分:3)
针对您的特定类型实施的operator new
仅用于您的类型元素动态分配 new
的表达式,例如Test *p = new Test;
。现在make_shared
动态分配您的类型的对象,而是缓冲区,其中包含共享计数的足够信息(其中包括计数器,删除器和一些额外的碎片)和你的对象。
然后使用 placement-new 来调用对象的构造函数。请注意,在这种情况下, placement new 不分配内存,只有C ++中有趣的语法才能在已分配的内存块上调用构造函数。这可能实际上是混淆的原因,因为new
表达式,您的operator new
和展示位置新是碰巧共享名称的三个不同概念。