6

我第一次使用 boost::make_shared 创建共享指针指向的对象。主要是因为我们的代码太慢了,单次分配确实有助于提高性能。

在“硬手动方式”修复了一些内存泄漏之后,我决定通过覆盖所有相关类的新运算符来实现一个简单的内存泄漏检测器,只是为了计算哪些对象在我们的应用程序的特定点仍然存在。我之前已经实现过几次,并且惊讶地发现我的代码不再检测到任何对象。

我认为我所要做的就是覆盖“placement new”而不是“normal” operator new,因为 make_shared 的 boost 网站文档中的以下内容:

“效果:分配适合 T 类型对象的内存,并通过放置 new 表达式 new( pv ) T() 或 new( 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 类的放置 new 运算符?还是我误会了?

我当然可以在本地复制 boosts 实现并添加对放置新操作符的调用。但是,这是否合适,还是会违反放置 new 的预期语义?

提前感谢您的时间和帮助。

4

3 回答 3

8

作为 的来源make_shared,它使用全局放置new运算符,而不是您的类提供的新运算符。

::new( pv ) T();

不幸的是(至少在 OS X 上)根据标准),您无法定义自己的全局放置 new 运算符。看起来这allocate_shared更符合您正在寻找的内容。

编辑

另一种选择可能是实际编写一个版本,make_shared该版本使用类的位置 new 而不是全局位置。它只有大约 10 行代码,只要您遵守原始代码的许可,应该没问题。

于 2012-03-12T22:16:02.727 回答
4

您可能不会替换placement new(§18.4.​1.3,参见例如this question),所以给出的输出看起来不错。

作为修改 Boost 标头的替代方法,您可以查看 Valgrind 等外部​​工具。

于 2012-03-12T22:14:59.413 回答
3

operator new为你的特定类型实现的只会用在你的类型的元素被动态分配的表达式上new,例如Test *p = new Test;. 现在make_shared不会动态分配您类型的对象,而是为共享计数(包括计数器、删除器和一些额外的位和部分)和您的对象保存足够信息的缓冲区。

然后它使用placement-new来调用对象的构造函数。请注意,在这种情况下放置 new不是分配内存,它只是 C++ 中有趣的语法,用于在已分配的内存块上调用构造函数。这实际上可能是混淆的根源,因为new表达式、youroperator newplacement-new是碰巧共享一个名称的三个不同概念。

于 2012-03-12T22:26:37.777 回答