我应该外包分配算法吗? (RAII)
Should I outsource allocation algorithm? (RAII)
现在我的class有一个构造函数、复制构造函数和复制赋值运算符 其中 一开始都做同样的事情 (分配内存)。析构函数正在释放内存。
class Register
{
public:
Register()
{
_trampoline_address = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (_trampoline_address == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
//....
}
~Register()
{
if (_trampoline_address != nullptr)
{
debug(VirtualFree(_trampoline_address, 0, MEM_RELEASE));
}
}
Register(const Register& other)
{
_trampoline_address = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (_trampoline_address == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
//...
}
Register& operator= (const Register& other)
{
_trampoline_address = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (_trampoline_address == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
//....
}
private:
BYTE* _trampoline_address;
static const int _trampoline_size = 20;
};
我考虑过外包分配算法因为我使用它 3 次,但我不希望相同 class 类型的其他实例可以访问它函数。
那么在 RAII class 的 3 个函数中分配内存的正确解决方案是什么?
您可以执行以下两项操作之一:
- 创建一个
private
函数,然后在每个构造函数中调用它。这仍然允许 class 的其他成员函数在构造后调用该函数,但这应该没问题(只是不要调用它!)。 Subclasses 不能调用 class 的私有成员,所以你应该没问题。
- 使用delegated constructors。在一个构造函数中执行分配,然后从其他构造函数调用该构造函数。显然,没有人可以显式调用构造函数,因此您的顾虑应该得到解决。可能的警告:您将需要使用 C++11 兼容的编译器。
您可以创建一个 private static
函数来帮助分配内存:
static BYTE* allocate()
{
BTYE* ptr = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (ptr == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
return ptr;
}
那么,构造函数可以简化为:
Register() : _trampoline_address(allocate()) { }
Register(const Register& other) : _trampoline_address(allocate()) { }
但是,复制赋值运算符需要多做一些工作。首先,从您发布的代码中不清楚赋值运算符的语义是什么。您应该将数据从 RHS 复制到 LHS 吗?如果不是,RHS 在赋值操作中起什么作用?您应该如何处理 LHS 拥有的内存?
Register& operator= (const Register& other)
{
// Prevent messing with memory when dealing with self-assignment.
if ( this != &other )
{
// Deal with the semantics of the operation.
}
return *this;
}
struct Register {
Register():
_trampoline_address(allocate())
{}
Register(Register const& o):
Register() // forward to default ctor
{
copy_data_from(o);
}
~Register() {
if (_trampoline_address)
debug(VirtualFree(_trampoline_address, 0, MEM_RELEASE));
}
Register& operator= (const Register& o) {
if (this != std::addressof(o))
copy_data_from(o);
return *this;
}
private:
void copy_data_from(Register const& o) {
Assert(_tranpoline_address);
// ...
}
static BYTE* allocate() {
return reinterpret_cast<BYTE*>(
VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
);
}
BYTE* _trampoline_address;
static const/*expr*/ int _trampoline_size = 20;
};
只有一个调用allocate
,但我还是放在了static private
方法里,因为比较乱
我也写了copy_data_from
,因为它会用到两次(一次在copy-ctor,一次在assignment)
我个人很想 Register()
给你留下一个空的缓冲区,只有在使用时才填充。读取函数必须检查 nullptr
和 allocate()
是否丢失:但无论如何它们必须检查 nullptr
。结果是更高效的 move-ctor 和 move-assign,创建空数组(和稍后填充)效率更高,等等。
在这种情况下,allocate()
更有用。您甚至可以使用 ensure_allocated()
,它会在 nullptr
.
时初始化 _tranpoline_address
现在我的class有一个构造函数、复制构造函数和复制赋值运算符 其中 一开始都做同样的事情 (分配内存)。析构函数正在释放内存。
class Register
{
public:
Register()
{
_trampoline_address = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (_trampoline_address == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
//....
}
~Register()
{
if (_trampoline_address != nullptr)
{
debug(VirtualFree(_trampoline_address, 0, MEM_RELEASE));
}
}
Register(const Register& other)
{
_trampoline_address = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (_trampoline_address == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
//...
}
Register& operator= (const Register& other)
{
_trampoline_address = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (_trampoline_address == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
//....
}
private:
BYTE* _trampoline_address;
static const int _trampoline_size = 20;
};
我考虑过外包分配算法因为我使用它 3 次,但我不希望相同 class 类型的其他实例可以访问它函数。
那么在 RAII class 的 3 个函数中分配内存的正确解决方案是什么?
您可以执行以下两项操作之一:
- 创建一个
private
函数,然后在每个构造函数中调用它。这仍然允许 class 的其他成员函数在构造后调用该函数,但这应该没问题(只是不要调用它!)。 Subclasses 不能调用 class 的私有成员,所以你应该没问题。 - 使用delegated constructors。在一个构造函数中执行分配,然后从其他构造函数调用该构造函数。显然,没有人可以显式调用构造函数,因此您的顾虑应该得到解决。可能的警告:您将需要使用 C++11 兼容的编译器。
您可以创建一个 private static
函数来帮助分配内存:
static BYTE* allocate()
{
BTYE* ptr = reinterpret_cast<BYTE*>(VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (ptr == nullptr)
{
throw my_exception("Could not allocate memory for trampoline function.");
}
return ptr;
}
那么,构造函数可以简化为:
Register() : _trampoline_address(allocate()) { }
Register(const Register& other) : _trampoline_address(allocate()) { }
但是,复制赋值运算符需要多做一些工作。首先,从您发布的代码中不清楚赋值运算符的语义是什么。您应该将数据从 RHS 复制到 LHS 吗?如果不是,RHS 在赋值操作中起什么作用?您应该如何处理 LHS 拥有的内存?
Register& operator= (const Register& other)
{
// Prevent messing with memory when dealing with self-assignment.
if ( this != &other )
{
// Deal with the semantics of the operation.
}
return *this;
}
struct Register {
Register():
_trampoline_address(allocate())
{}
Register(Register const& o):
Register() // forward to default ctor
{
copy_data_from(o);
}
~Register() {
if (_trampoline_address)
debug(VirtualFree(_trampoline_address, 0, MEM_RELEASE));
}
Register& operator= (const Register& o) {
if (this != std::addressof(o))
copy_data_from(o);
return *this;
}
private:
void copy_data_from(Register const& o) {
Assert(_tranpoline_address);
// ...
}
static BYTE* allocate() {
return reinterpret_cast<BYTE*>(
VirtualAlloc(nullptr, _trampoline_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
);
}
BYTE* _trampoline_address;
static const/*expr*/ int _trampoline_size = 20;
};
只有一个调用allocate
,但我还是放在了static private
方法里,因为比较乱
我也写了copy_data_from
,因为它会用到两次(一次在copy-ctor,一次在assignment)
我个人很想 Register()
给你留下一个空的缓冲区,只有在使用时才填充。读取函数必须检查 nullptr
和 allocate()
是否丢失:但无论如何它们必须检查 nullptr
。结果是更高效的 move-ctor 和 move-assign,创建空数组(和稍后填充)效率更高,等等。
在这种情况下,allocate()
更有用。您甚至可以使用 ensure_allocated()
,它会在 nullptr
.
_tranpoline_address