gpt4 book ai didi

c++ - 抽象类作为接口(interface),没有 vtable

转载 作者:可可西里 更新时间:2023-11-01 16:39:13 25 4
gpt4 key购买 nike

我想创建一个抽象类来定义类的一些方法。其中一些应该由基类 (Base) 实现,一些应该在 Base 中定义但被 Derived 覆盖,还有一些应该在 Base 中是纯虚拟的以强制在 Derived 中定义。

这当然是抽象类的用途。但是,我的应用程序只会直接使用 Derived 对象。因此,编译器应该在编译时准确地知道要使用哪些方法。

现在,因为这段代码将在 RAM 非常有限的微 Controller 上运行,所以我很想避免实际使用带有 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; }

};

没有虚表

这没有虚表,这就是我想要的

int main() {
Derived d;
d.thisMustBeDefined();
}

是 vtable 1

由于我草率的编码,我错误地强制编译器使用多态性,因此需要一个 vtable。我怎样才能让这个案例抛出错误?

int main() {
Base * d;
d = new Derived();
d->thisMustBeDefined();
}

是 vtable 2

这里我在任何时候都没有引用“Base”类,因此编译器应该知道所有方法都是在编译时预先确定的。但是它仍然创建一个 vtable。这是我希望能够通过编译错误检测到这一点的另一个示例。

int main() {
Derived * d;
d = new Derived();
d->thisMustBeDefined();
}

换句话说,如果我编写的代码导致编译器为我的类生成 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; }

};

如果函数未在 Derived 中实现,为了使编译器错误更具可读性,您可以使用 this answer 中提供的简单静态检查。 :

#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));

并将静态检查添加到 Base 构造函数中:

Base() {
static_assert(thisMustBeDefined<Der>::thisMustBeDefined,
"Derived class must implement thisMustBeDefined");
}

虽然在小型设备上工作时应该考虑一个缺点,并且您同时有多个版本的 DerivedBase 中的代码将被复制每个 Derived 实例。

因此,您必须决定对于您的用例而言,什么是更重要的限制。

正如@ChrisDrew 在他们的 comment 中指出的那样,将 thisCouldBeOverwritten()thisWillBeUsedAsIs() 函数移动到 Base 模板类派生的另一个基类将有助于解决该问题。

关于c++ - 抽象类作为接口(interface),没有 vtable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40265936/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com