OVERVIEW
- 多态/虚函数/虚函数表
- 1.虚函数引入后类发生的变化?
- 2.虚函数表的生成时机和生成原因?
- 3.虚函数表指针赋值的时机?
- 4.类对象在内存中的布局?
- 5.虚函数的工作原理和多态性的体现?
- 6.其他问题
多态/虚函数/虚函数表
- non-virtual:不希望derived class 重新定义该它
- virtual:希望derived class重新定义它,并且你对它已经有了默认的定义
- pure-virtual:希望derived class一定要重新定义它
1.虚函数引入后类发生的变化?
class A {
public:
void func1() {}
void func2() {}
public:
virtual void vfunc() {}
};
sizeof(A) = 4;//x86
sizeof(A) = 8;//linuxgcc
当一个或者多个虚函数加入到类中之后,编译器会向类中插入一个看不见的成员变量void *vptr
虚函数表指针(virtual table ptr),
2.虚函数表的生成时机和生成原因?
当某个类中存在至少一个虚函数的时候,在编译期间编译器就会为类A生成一个虚函数表 vtbl
(virtual table),
虚函数表会一直伴随着类经过编译、链接、生成可执行文件、装载到内存中来,
3.虚函数表指针赋值的时机?
对于有虚函数的类在编译期时,编译器向类的构造函数中安插为vptr赋值的语句,在创建类对象时使得vptr指向类的vtbl,
4.类对象在内存中的布局?
class A {
public:
void func1() {}
void func2() {};
public:
virtual void vfunc1() {}
virtual void vfunc2() {}
virtual ~A() {}
private:
int m_a;
int m_b;
};
sizeof(A) = 12;
类A在内存中的布局:
5.虚函数的工作原理和多态性的体现?
多态性:多态必须存在虚函数,没有虚函数绝不可能存在多态,并且只有调用虚函数时才有存在多态性的可能,
从代码上看多态的体现:
- 父类中有虚函数、子类中也有该同名的虚函数,
- 当通过父类指针new子类对象 or 通过父类引用绑定子类对象时,
- 如果用父类指针来调用该虚函数,那么调用的是子类的虚函数,
class Base {
public:
virtual void myvirfunc() {}
};
Base *ptr = new Base();
ptr->myvirfunc();//多态
Base base;
base.myvirfunc();//非多态
Base *pbase = &base;
pbase->myvirfunc();//多态
从表现形式上看多态的体现:
- 程序中既存在父类也存在子类,父类中必须含有虚函数,子类中也必须重写父类中的虚函数,
- 父类指针指向子类对象,或者父类引用绑定/指向子类对象,
- 当通过父类的指针or引用,调用子类中重写的虚函数时,就能看出多态性的表现了(调用的是子类的虚函数),
class Derive : public Base {
public:
virtual void myvirfunc();
};
//父类指针指向子类对象1
Derive derive;
Base *pbase1 = &derive;
pbase1->myvirfunc();
//父类指针指向子类对象2
Base *pbase2 = new Derive();
pbase2->myvirfunc();
//父类引用绑定子类对象
Derive derive2;
Base &refer_base = derive2;
refer_base.myvirfunc();
6.其他问题
- 问:当进行多重继承时,子类对象有几个虚函数表指针?子类有几个虚函数表?
- 答:在进行多重继承时,子类对象会包含每个父类的成员变量和虚函数表指针。
如果每个父类都有虚函数,那么每个父类都会有一个虚函数表,子类对象会包含每个父类的虚函数表指针,因此子类对象会有多个虚函数表指针,每个指针指向相应的虚函数表。
如果某个父类没有虚函数,那么子类对象就不会包含该父类的虚函数表指针,只会包含其他父类的虚函数表指针和成员变量。
- 问:如果将虚继承(虚基类)融入进来,这时又多出来了虚基类表指针和虚基类表,那么虚基类表指针在对象内存中的布局?
- 浅谈C++类中的内存布局:https://www.zhihu.com/tardis/zm/art/380147337?source_id=1005