6

我想强制我的对象在堆栈上以强制执行非常严格的语义并解决一些生命周期问题。我已经阅读了几篇关于如何做到这一点的文章,并最终将其设为operator new私有(或删除)。new直接使用时,这似乎按预期工作,但make_shared编译得很好。

#include <boost/smart_ptr.hpp>

class A
{
private:
   void *operator new( size_t );
   void operator delete( void* );
   void *operator new[]( size_t );
   void operator delete[]( void* );
};

int main()
{
//  A* a = new A;      // Correctly produces compile error
    boost::shared_ptr<A> a2 = boost::make_shared<A>();
}

直接使用new A会按预期给我这个错误:

error: ‘static void* A::operator new(size_t)’ is private

我猜这make_shared是可行的,因为它使用了placement new 运算符,但我找不到任何讨论如何禁止此操作的文章。我想出的最佳解决方案是显式删除模板专业化make_shared

namespace boost
{
    template<>
    shared_ptr<A> make_shared<A>() = delete;
};

这显然是非常具体的boost::make_shared。这真的是最好的方法吗?

4

2 回答 2

4

的放置形式new很容易处理——它们只是带有额外的参数。例如,简单的放置形式是

void* operator new(std::size_t, void*);

请注意,18.6.1.3 禁止在全局范围内重新定义这些;但是,为您的特定类型重新定义(或删除/使其无法访问)它们应该没有问题。

不幸的是,make_shared使用 scoped ::new (pv) T(std::forward<Args>(args)...)。正如我所提到的,你不能乱用新的全局布局。所以你不能在编译时阻止它,任何运行时陷阱都会是一个黑客(检查this指针以查看它是否在堆栈的边界内)。

于 2012-12-25T01:52:33.830 回答
2

您不能通过仅使任何运算符不可访问来强制类的对象始终在堆栈上:可以在堆栈上构造的任何对象也可以作为成员嵌入到另一个对象中。即使您的原始类可能难以在堆上分配,包含类也不会。我认为这是在以下情况下发生的情况boost::make_shared():在内部,它可能分配了一些记录,其中包含其管理数据和实际分配的对象。或者,它可以使用来自某种分配器的分配函数,这些分配器不映射到类型,operator new()而是使用它自己的operator new()重载。

我不确定是否可以防止堆分配(至少,当对象嵌入到另一个对象中时),但这样做的任何方法都需要使构造函数不可访问(很可能private)并使用某种工厂函数,可能与移动相结合。另一方面,如果您可以移动一个对象,那么您就有一个可访问的构造函数,并且没有什么可以阻止该对象被移动到堆上的一个对象中。

如果您特别想阻止使用std::make_shared()具体类型(或者boost::make_shared()尽管我不能引用专门用于后者的规则),您可以专门std::make_shared()化:根据 17.6.4.2.1 [namespace.std] 第 1 段,用户是如果涉及用户定义的类型,则允许专门化任何模板(除非另有说明)。因此,您可以防止Astd::make_shared():

class A
{
public:
    A();
    A(int);
};

namespace std
{
    template <> std::shared_ptr<A> make_shared<A>() = delete;
    template <> std::shared_ptr<A> make_shared<A, int>(int&&) = delete;
}
namespace boost
{
    template <> boost::shared_ptr<A> make_shared<A>() = delete;
    template <> boost::shared_ptr<A> make_shared<A, int>(int&&) = delete;
}

显然,如果你有多个构造函数,A你可能需要添加更多的特化。...如果您的类型恰好是类模板或构造函数是模板,那么您将不走运:您不能部分专门化函数模板。

关于您关于展示位置的问题new(可能会或可能不会被 使用make_shared()):展示位置new(和delete)签名如下:

void* operator new(size_t, void*) noexcept;
void* operator new[](size_t, void*) noexcept;
void  operator delete(void*, void*) noexcept;
void  operator delete[](void*, void*) noexcept;

(见 18.6 [support.dynamic] 第 1 段)。不过,我怀疑使它们无法访问会对您有所帮助。

于 2012-12-25T02:15:49.340 回答