通过make_shared停止堆分配
Stop heap allocation via make_shared
我想强制我的对象在堆栈上,以强制执行非常严格的语义并解决一些生命周期问题。我读了几篇关于如何做到这一点的文章,并将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
正在工作,因为它正在使用放置新操作符,但我找不到任何讨论如何禁止这种情况的文章。我想到的最佳解决方案是显式地删除make_shared
的模板专门化
namespace boost
{
template<>
shared_ptr<A> make_shared<A>() = delete;
};
这显然是boost::make_shared
特有的。这真的是最好的方法吗?
new
的位置形式很容易处理——它们只是附带了额外的参数。例如,简单的放置表单是
void* operator new(std::size_t, void*);
注意18.6.1.3禁止在全局范围内重定义这些;但是,为您的特定类型重新定义(或删除/使不可访问)它们应该没有问题。
不幸的是,make_shared
使用了::new (pv) T(std::forward<Args>(args)...)
的作用域。正如我提到的,你不允许乱动全局布局new。因此,您无法在编译时阻止它,并且任何运行时陷阱都将是一种攻击(检查this
指针以查看它是否在堆栈的边界内)。
不能通过使任何操作符仅不可访问来强制类的对象始终位于堆栈上:任何可以在堆栈上构造的对象也可以作为成员嵌入到另一个对象中。即使您的原始类可能会反对在堆上分配,但包含类不会。我认为这就是boost::make_shared()
的情况:在内部,它可能分配一些记录,其中包含其管理数据和实际分配的对象。或者,它可以使用来自某种分配器的分配函数,该分配器不映射到类型的operator new()
,而是使用自己的operator new()
重载。
我不确定是否有可能阻止堆分配(至少,当对象嵌入到另一个对象中时),但是这样做的任何方法都需要使构造函数不可访问(最有可能的是private
)并使用某种工厂函数,可能与移动相结合。另一方面,如果你可以移动一个对象,那么你就有了一个可访问的构造函数,并且没有什么可以阻止这个对象被移动到堆上的一个对象中。
如果你特别想防止对具体类型使用std::make_shared()
(或boost::make_shared()
,尽管我不能引用后者专门化的规则),你可以专门化std::make_shared()
:如果模板包含用户定义的类型,则允许用户专门化任何模板(除非另有说明)。因此,您可以防止A
与std::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。但是,我怀疑让它们不可访问对你有什么帮助。
- 将数组的地址分配给变量并删除
- vector.resize()中的分配错误
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- Win32编译器选项和内存分配
- 函数中堆分配的效果与缺少堆分配的情况
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 获取字符串的长度并将其分配给数组
- 将地址分配给本地指针后,公共对象的变量将消失
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- 我在二维向量中是否正确分配了内存
- 正在尝试重载二进制搜索树分配运算符
- GlobalAlloc而不是其他分配方法
- 自定义先决条件对移动分配运算符有效吗
- 我可以重新分配/覆盖std::字符串吗
- 在c++中使用动态分配的问题
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 为什么我可以使用比分配的内存更多的内存
- 使用RAII在给定次数的迭代后重新分配资源