我应该外包分配算法吗? (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 个函数中分配内存的正确解决方案是什么?

您可以执行以下两项操作之一:

  1. 创建一个private函数,然后在每个构造函数中调用它。这仍然允许 class 的其他成员函数在构造后调用该函数,但这应该没问题(只是不要调用它!)。 Subclasses 不能调用 class 的私有成员,所以你应该没问题。
  2. 使用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() 给你留下一个空的缓冲区,只有在使用时才填充。读取函数必须检查 nullptrallocate() 是否丢失:但无论如何它们必须检查 nullptr 。结果是更高效的 move-ctor 和 move-assign,创建空数组(和稍后填充)效率更高,等等。

在这种情况下,allocate() 更有用。您甚至可以使用 ensure_allocated(),它会在 nullptr.

时初始化 _tranpoline_address