是否有可能知道基类中的派生对象大小?

时间:2015-03-30 21:13:36

标签: c++

我要求必须在特殊内存插槽中使用placement new运算符初始化它们自己派生类的某些对象族。所有这些都将来自共同基础。 问题是此类对象存在大小限制。 所以我想在派生对象大小上放置静态断言,但我不想在每个后代中打扰静态断言。 我的问题是:有没有办法将这个断言放入基数或使用任何其他技巧来检查派生类声明之外的大小? 将基类设计为模板是可以接受的。

2 个答案:

答案 0 :(得分:4)

听起来像CRTP的另一个用例:

constexpr std::size_t limit = 42;

template <typename Derived>
class Base {
  ~Base() {
    static_assert(sizeof(Derived) <= limit, "Derived class is too big.");
  }
};

class Foo : Base<Foo> {};

当然,如果你需要一个公共基类,你可以在CRTP大小检查下注入一个:

class Base {
  // ...
};

constexpr std::size_t limit = 42;

template <typename Derived>
class SizeCheck : public Base {
  ~SizeCheck() {
    static_assert(sizeof(Derived) <= limit, "Derived class is too big.");
  }
};

class Foo : SizeCheck<Foo> {};

如果您的目标不仅仅是保存一些按键,而且还要防止那些积极尝试打破支票的人:

template <typename>
class SizeCheck;

class Base {
  // ...
private:
  // Only allow SizeCheck to derive from Base
  ~Base() = default;
  template <typename>
  friend class SizeCheck;
};

constexpr std::size_t limit = 42;

template <typename Derived>
class SizeCheck : public Base {
private:
  // Only allow Derived to derive from SizeCheck<Derived>
  friend Derived;
  ~SizeCheck() {
    static_assert(sizeof(Derived) <= limit, "Derived class is too big.");

    // Ensure that Derived is actually derived from SizeCheck
    static_assert(std::is_base_of<SizeCheck, Derived>(),
                  "Parameter to SizeCheck must be derived from SizeCheck.");

    // Require Derived to be final so that no one can sidestep 
    // the size check. (Uses C++14 std::is_final)
    static_assert(std::is_final<Derived>(),
                  "Nice try; parameter to SizeCheck must be final.");
  }
};

这一切都变得有点扭曲。从Base派生的类的大小确实不是问题,它可能试图将对象太大而无法放入静态大小的缓冲区中。通过保护缓冲区而不是限制派生类(DEMO)来从另一端处理问题可能更简单:

struct placement_delete {
  template <typename T>
  void operator()(T* ptr) {
    ptr->~T();
  }
};

template <typename T>
using placement_ptr = std::unique_ptr<T, placement_delete>;

class Base {
  // ...
};

template <std::size_t N, std::size_t Align = 0>
class buffer {
public:
  template <typename T, typename...Args>
  placement_ptr<T> emplace(Args&&...args) {
    static_assert(std::is_base_of<Base, T>(),
                  "Only classes derived from Base can go in a buffer.");
    static_assert(sizeof(T) <= sizeof(space_),
                  "Type is too big for buffer.");
    static_assert(alignof(decltype(space_)) % alignof(T) == 0,
                  "Buffer alignment is insufficient for type.");
    return placement_ptr<T>{::new(&space_) T(std::forward<Args>(args)...)};
  }
private:
  typename std::conditional<!Align,
    typename std::aligned_storage<N>::type,
    typename std::aligned_storage<N, Align>::type
  >::type space_;
};

答案 1 :(得分:2)

基类没有办法直接知道派生类的大小 - 这样做需要知道派生类的内容和数据布局,这就要求类在同一个编译单元中 - 你可能需要使用模板或宏来确保它发生。

您可以覆盖基类的operator new - 这也适用于所有不会覆盖operator new的派生类。有一些方法可以找到,例如使用返回sizeof(*this)的虚函数(但是要求对象已经创建 - 你不能从构造函数或构造之前调用它,所以如果你想使用新的放置,这将无法工作)。

当然,如果你掌握了源代码,你可以做static_assert(sizeof(someDerivedClass) > some_value) - 但这并不是因为没有被希望这样做的人打破。