Cha*_*ieB 9 c++ inheritance abstract-class interface c++11
我想创建一个定义类的一些方法的抽象类.其中一些应该由基类(Base)实现,一些应该在Base中定义但是由Derived覆盖,而其他应该在Base中是纯虚拟的,以强制Derived中的定义.
这当然是抽象类的用途.但是,我的应用程序只会直接使用Derived对象.因此,编译器应该在编译时确切地知道要使用哪些方法.
现在,因为这段代码将在内存非常有限的微控制器上运行,所以我很想避免实际使用带有vtable的虚拟类.从我的测试来看,似乎编译器足够聪明,除非必须,否则不能制作vtable,至少在某些情况下.但是我被告知永远不要相信编译器:是否有可能使这成为编译的必要条件?
以下是一些代码示例:
class Base {
public:
Base() {}
virtual ~Base() {};
virtual int thisMustBeDefined() = 0;
virtual int thisCouldBeOverwritten() { return 10; }
int thisWillBeUsedAsIs() { return 999; }
};
class Derived : public Base {
public:
Derived() {}
~Derived() {}
int thisMustBeDefined() { return 11; }
};
Run Code Online (Sandbox Code Playgroud)
这没有vtable,是我想要的
int main() {
Derived d;
d.thisMustBeDefined();
}
Run Code Online (Sandbox Code Playgroud)
由于我的草率编码,我错误地强迫编译器使用多态,因此需要一个vtable.如何让这个案例抛出错误?
int main() {
Base * d;
d = new Derived();
d->thisMustBeDefined();
}
Run Code Online (Sandbox Code Playgroud)
在这里,我没有在任何时候提到类"Base",因此编译器应该知道所有方法都是在编译时预先确定的.然而,它仍然创造了一个vtable.这是我希望能够通过编译错误检测到这一点的另一个例子.
int main() {
Derived * d;
d = new Derived();
d->thisMustBeDefined();
}
Run Code Online (Sandbox Code Playgroud)
换句话说,如果我编写导致编译器为我的类生成vtable的代码,即使用多态,我希望它是编译器错误.
正如在评论中已经提到的那样,您可以使用CRTP(又名静态多态)来避免创建vtable:
template <typename Der>
class Base {
public:
Base() {}
~Base() {};
int thisMustBeDefined() {
// Will fail to compile if not declared in Der
static_cast<Der*>(this)->thisMustBeDefined();
}
int thisCouldBeOverwritten() { return 10; }
int thisWillBeUsedAsIs() { return 999; }
};
class Derived : public Base<Derived> {
public:
Derived() {}
~Derived() {}
int thisMustBeDefined() { return 11; }
// Works since you call Derived directly from main()
int thisCouldBeOverwritten() { return 20; }
};
Run Code Online (Sandbox Code Playgroud)
如果在Derived您未实现函数时使编译器错误更具可读性,可以使用此答案中提供的简单静态检查:
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
template<typename T, T> struct helper; \
template<typename T> \
static std::uint8_t check(helper<signature, &funcName>*); \
template<typename T> static std::uint16_t check(...); \
public: \
static \
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
}
DEFINE_HAS_SIGNATURE(thisMustBeDefined, T::thisMustBeDefined, int(*)(void));
Run Code Online (Sandbox Code Playgroud)
并将静态检查添加到Base构造函数:
Base() {
static_assert(thisMustBeDefined<Der>::thisMustBeDefined,
"Derived class must implement thisMustBeDefined");
}
Run Code Online (Sandbox Code Playgroud)
虽然在处理小型设备时应该考虑一个缺点,并且您一次拥有更多版本Derived,但Base每个Derived实例的代码都将重复.
因此,您必须确定对您的用例更重要的限制是什么.
正如@ChrisDrew在他们的评论中指出的那样,将thisCouldBeOverwritten()和thisWillBeUsedAsIs()函数移动到Base模板类派生的另一个基类会促进这个问题.