我有一些类(大多数是抽象的,虚拟继承):
class A{
public:
virtual void f1() = 0;
virtual void f2() = 0;
};
class B : virtual public A{
public:
virtual void f3() = 0;
};
class MyA : virtual public A{
public:
virtual void f1(){ ... }
virtual void f2(){ ... }
};
class MyB : virtual public B, virtual public MyA{
public:
void f3(){ ... }
};
现在我使用它们:
B * object = new MyB();
object->f1(); //declared in A, imp. in MyA
object->f3(); //declared in B, imp. in MyB
一切正常,代码是好的"对我来说(我可以快速从MyB
切换到YourB
只更改1行。)
它使用多少额外内存,与下面列出的类似代码相比(结果相同,结构不同)?
我对内存布局/ vTables不太满意,所以请以简单的方式向我解释 - 我想知道,如果我的应用程序将花费更多资源(内存)和如果可执行文件会更慢?
我将该代码与那个代码进行比较:
class MyA{
public:
virtual void f1(){ ... }
virtual void f2(){ ... }
};
class MyB : public MyA{
public:
void f3(){ ... }
};
MyB * object = new MyB();
object->f1(); //declared in MyA, imp. in MyA
object->f3(); //declared in MyB, imp. in MyB
sizeof(object)
在两个示例中都返回4(Win x32,Visual Studio本机编译器),但我不确定它是否具有权威性。也许它不算数 - 我不认为两个样本都是100%相等。
答案 0 :(得分:2)
这取决于实现,但通常需要虚拟继承:
B*
到A*
的调整将取决于同一对象中的哪些其他子对象也从{ {1}}。我认为这可以存储在 vtable 中,而不是存储在对象本身中,因此开销可能是每个类而不是每个对象。对于内存使用,您可以通过打印A
来衡量实现中的每个对象开销。除非你有大量的课程,否则任何每班的开销都可能微不足道。
答案 1 :(得分:1)
在开始之前,您的担忧被称为过早优化。在尺寸和空间之前还需要担心其他问题:正确性和稳健性。
继承通常通过虚函数表实现,实质上是函数的地址表。因此额外代码空间的数量取决于虚函数的数量。
使用跳转表执行虚函数。通常的做法是使用功能表中的值加载程序计数器。这通常是2个装配说明。
函数表的可变空间量可能小于结构中对齐填充所浪费的内存量。浪费的执行时间小于调用函数的开销。
编辑1:
顺便说一下,汇编指令通常在1微秒或更短的时间内执行。所以通过函数表调用需要2微秒。将其与等待磁盘I / O或用户I / O进行比较。
这就是为什么它是一个过早的优化:在分析之前进行优化。在担心代码浪费和可变空间之前,先描述整个代码并注意瓶颈。
答案 2 :(得分:1)
对于常用的编译器,开销大致如下
sizeof(object)
在两种情况下都会返回4,因为您正在测量指向对象的指针的大小,而不是对象本身。无论指向哪个对象,指针大小都是相同的。