代码中有一些特殊的类和一些正常的类。我想把它们区分开来,因为特殊的班级需要给予不同的对待。所有这些特殊类都是基类(不是任何其他类的子类)
为了实现这一点,我在源代码中对特殊的class
es进行了标记,方法是用空的struct
插入继承:
struct _special {}; // empty class
class A : public _special { // A becomes special
...
};
class B { // 'B' remains normal
...
};
class D : public A { // 'D' becomes special due to 'A'
...
};
无论何时需要,我都可以使用is_base_of<Base,Derived>
找到隔离特殊类和正常类的方法。另一种方法是在特殊类中使用typedef
:
class A {
public: typedef something _special;
};
问题是,如果A
的子继承了多个类,那么将会有不明确的typedef
。
问题:添加这样的接口,如继承与空class _special
,它会伤害当前的代码在任何方面(例如对象结构,编译错误等)?
内存中对象的布局在c++标准中只有部分规定,但是大多数编译器使用某些约定。空类型将占用一点内存(因此它们将有一个内存地址,这将给它们的指针标识)。这个额外的内存位通常只有四个字节,在大多数情况下无需担心。另一方面,如果你从一个空类型继承,它不应该增加你的对象的大小,因为对象的其余部分将占用空间,所以无论如何它都会有一个地址。
如果你使用的是单继承对象,对象的第一个内存位将像第一个基类一样被布局,然后存储链中后面类的成员的内存。如果你有任何虚函数,也会有一个地方,可能在开始,为虚指针。如果要从一个类型派生另一个类型,通常需要遵循"三规则":虚析构函数、复制构造函数和复制赋值操作符。这样你就有了一个虚指针,这大概是4个字节,没什么大不了的。
如果你进入多重继承,那么你的对象在结构上开始变得非常复杂。它们将有指向自身不同部分的各种指针,以便函数可以找到它们正在查找的成员。
也就是说,要考虑是否要使用继承来建模。
对于简单的情况,大多数(如果不是所有的话)体面的编译器都实现了空基优化(EBO),这意味着您的对象大小不会通过继承空基而增长。然而,当一个类以多种方式从空基继承时,优化可能是不可能的,因为需要为同一类型的不同空基具有不同的地址。为了防止这种情况,通常将空基类作为接受派生类作为参数的模板,但这会使is_base_of
无法使用。
就我个人而言,我会在外部实现这种分类。模板专门化不会得到期望的结果,因为从special派生的类也被间接地认为是特殊的。看起来你用的是c++ 11,所以我会写:
std::false_type is_special( ... );
std::true_type is_special( A const* );
将is_base_of<T, _special>
替换为decltype( is_special( static_cast<T*>(0) ) )
。在c++ 03中,通过使用不同大小的分类函数返回类型,可以使用sizeof
技巧实现相同的目的:
typedef char no_type;
struct yes_type { no_type _[2]; };
no_type is_special( ... );
yes_type is_special( A const* );
将is_base_of<T, _special>
替换为sizeof( is_special( static_cast<T*>(0) ) ) == sizeof( yes_type )
。您可以将分类检查包装在helper类模板中。
不确定伤害或对象结构是什么意思(要详细说明吗?),但应该不会有编译器错误,从_special派生的类的实例化/构造函数不会改变,因为_special有默认构造函数,并且从性能角度来看,编译器可能会应用空基类优化。
也就是说,使用typedef来标记类可能是一个更好、更清晰、更可扩展的解决方案。就像A的子类继承了多个其他类一样模棱两可,这些类都继承自_special.