我知道对于任何具有虚函数的类或者从具有虚函数的类派生的类,编译器会做两件事.首先,它为该类创建一个虚拟表,然后,它将虚拟指针(vptr)放在该对象的基础部分中.在运行时,此vptr被分配并在对象实例化时开始指向正确的vtable.
我的问题是,在实例化过程中,这个vptr的确切位置是什么?这个vptr的赋值是否发生在构造函数之前/之后的对象的构造函数中?
class base {
public:
void virtual fn(int i) {
cout << "base" << endl;
}
};
class der : public base{
public:
void fn(char i) {
cout << "der" << endl;
}
};
int main() {
base* p = new der;
char i = 5;
p->fn(i);
cout << sizeof(base);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里定义的函数fn的签名与base类中fn()定义的函数的签名不同,der虽然函数名称相同.因此,der类中定义的base函数隐藏了类函数fn().所以der fn的类版本不能通过调用来p->fn(i)调用; 没事.
我的观点是,为什么sizeof类base或者der是4如果没有用的VTABLE的指针 …
如果我有一个类Base,至少有一个虚函数,并且一个Derived类从中单独继承,(uintptr_t)derived - (uintptr_t)static_cast<Base*>(derived)则保证(由Itanium ABI)为零,即使Derived不是标准布局.然而,在一般情况下,这不一定是真的(例如,多重继承).
是否有可能编写一个特征,可用于检测一个类是否是另一个类的主要基类?
来自Itanium ABI的有用部分:
http://refspecs.linux-foundation.org/cxxabi-1.83.html
主要基类
对于动态类,它与偏移0共享虚拟指针的唯一基类(如果有).它是第一个(以直接基类顺序排列)非虚拟动态基类(如果存在).
动态类
需要虚拟表指针的类(因为它或其基数具有一个或多个虚拟成员函数或虚拟基类).
我读了一个问题:C ++虚拟类继承对象大小问题,并且想知道为什么虚拟继承会在类中导致附加的vtable指针。
我在这里找到了一篇文章:https : //en.wikipedia.org/wiki/Virtual_inheritance
告诉我们:
但是,通常只能在运行时知道此偏移量,...
我在这里不了解与运行时相关的内容。完整的类继承层次结构在编译时就已经知道。我了解虚函数和基指针的用法,但是虚继承没有这种东西。
有人可以解释为什么某些编译器(Clang / GCC)使用vtable实现虚拟继承以及在运行时如何使用它吗?
顺便说一句,我也看到了这个问题:在虚拟继承的情况下使用vtable,但是它仅指向与虚拟函数相关的答案,这不是我的问题。
这不是关于"什么时候创建VTABLE?" .相反,什么时候应该初始化VPTR?它是在构造函数的开头/结尾还是在构造函数之前/之后?
A::A () : i(0), j(0) -->> here ?
{
-->> here ?
//...
-->> here ?
}
Run Code Online (Sandbox Code Playgroud) 这个问题不是关于C++语言本身(即不是关于标准),而是关于如何调用编译器来实现虚函数的替代方案.
实现虚函数的一般方案是使用指向指针表的指针.
class Base {
private:
int m;
public:
virtual metha();
};
Run Code Online (Sandbox Code Playgroud)
等价地说C会是这样的
struct Base {
void (**vtable)();
int m;
}
Run Code Online (Sandbox Code Playgroud)
第一个成员通常是指向虚拟函数列表等的指针(应用程序无法控制的内存中的一块区域).在大多数情况下,这会在考虑成员之前花费指针的大小等等.因此在大约4个字节的32位寻址方案中等等.如果在应用程序中创建了40k多态对象的列表,则大约为40k x在任何成员变量等之前4个字节= 160k字节.我也知道这恰好是C++编译中最快和最常见的实现.
我知道多重继承很复杂(尤其是虚拟类,即菱形结构等).
另一种方法是将第一个变量作为vptrs表的索引id(等效于C,如下所示)
struct Base {
char classid; // the classid here is an index into an array of vtables
int m;
}
Run Code Online (Sandbox Code Playgroud)
如果应用程序中的类总数小于255(包括所有可能的模板实例化等),则char足以保存索引,从而减少应用程序中所有多态类的大小(我排除了对齐问题)等).
我的问题是,在GNU C++,LLVM或任何其他编译器中是否有任何切换来执行此操作?或减少多态对象的大小?
编辑:我了解指出的对齐问题.还有一点,如果这是64位系统(假设为64位vptr),每个多态对象成员的成本约为8字节,那么vptr的成本就是内存的50%.这主要涉及大量创建的小型多态,所以我想知道如果不是整个应用程序,这个方案是否至少可以用于特定的虚拟对象.
c++ compiler-construction micro-optimization compiler-optimization vptr
对于某些编译器,如果一个类具有虚函数,则可以使用其对象的第一个字节的地址来访问其 vptr。例如,
\n\nclass Base{\npublic:\n virtual void f(){cout<<"f()"<<endl;};\n virtual void g(){cout<<"g()"<<endl;};\n virtual void h(){cout<<"h()"<<endl;};\n};\n\nint main()\n{ \n Base b;\n\n cout<<"Address of vtbl\xef\xbc\x9a"<<(int *)(&b)<<endl;\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我知道它取决于不同的编译器行为。既然存在 vptr 被存储为第一个条目的情况,那么这样做有什么好处呢?这是否有助于提高性能,或者仅仅是因为使用 &b 更容易访问 vbtl?
\n我可以理解为什么dynamic_cast在这种情况下有效:
#include <iostream>
struct A{
virtual ~A() = default;
};
struct B {
virtual ~B() = default;
};
struct C : A, B{};
void f(const A &a) {
if(auto p = dynamic_cast<const B*>(&a))
std::cout << "a is a B" << std::endl;
}
int main() {
f(C{});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是为什么如果你从B中删除多态,它仍然有效:
#include <iostream>
struct A{
virtual ~A() = default;
};
struct B {
};
struct C : A, B{};
void f(const A &a) {
if(auto p = dynamic_cast<const B*>(&a))
std::cout << …Run Code Online (Sandbox Code Playgroud) 在调试程序的核心转储之一时,我遇到了多态的包含对象失去其VPTr的情况,我可以看到它指向NULL.
当一个对象丢失其VPTr时可能出现的情况.
先谢谢,Brijesh
有人可以解释这个不同类的虚拟表是如何存储在内存中的吗?当我们使用指针调用函数时,他们如何使用地址位置调用函数?我们可以使用类指针获取这些虚拟表内存分配大小吗?我想看看一个类的虚拟表使用了多少内存块.我怎么能看到它?
class Base
{
public:
FunctionPointer *__vptr;
virtual void function1() {};
virtual void function2() {};
};
class D1: public Base
{
public:
virtual void function1() {};
};
class D2: public Base
{
public:
virtual void function2() {};
};
int main()
{
D1 d1;
Base *dPtr = &d1;
dPtr->function1();
}
Run Code Online (Sandbox Code Playgroud)
谢谢!提前
c++ ×10
vptr ×10
vtable ×4
constructor ×2
overriding ×2
dynamic-cast ×1
itanium-abi ×1
linux ×1
performance ×1
type-traits ×1