虚函数
类的对象模型
class A{
public:
void f1(){}
void f2(){}
virtual void f3(){}
virtual void f4(){}
};
class B{
public:
void f1(){}
void f2(){}
};
class C{
};
int main()
{
A a;
B b;
C c;
std::cout<<sizeof(a)<<std::endl;
std::cout<<sizeof(b)<<std::endl;
std::cout<<sizeof(c)<<std::endl;
return 0;
}
输出:
我们可以发现空类和只有多个函数的类的对象占的字节都是1
多个虚函数的类的对象占得字节都是8(不同编译环境,这个值不同)
这是因为在一个类中引入一个或多个虚函数时,编译器会插入一个虚函数表指针vptr
且每一个有虚函数的类都有一个虚函数表
类似于如下代码:
class A{
public:
void* vptr;
};
因为成员变量是占用类的对象的内存空间的,所以sizeof(a)=8
在根据下面这个代码解释一下类A的对象在内存中的布局
class A{
private:
int m_a;
int m_b;
public:
void func1(){}
void func2(){}
virtual void vfunc(){};
virtual void vfunc2(){}
virtual ~A(){}
};
int main()
{
A a;
std::cout<<sizeof(a)<<std::endl; //输出结果为16,因为两个int(8)+一个virtual(8)
return 0;
}
虚函数表指针指向虚函数表,虚函数表并不占用类的对象的内存,就像func1()和func()不占用内存一样
多态的使用
# include<iostream>
class Base{
public:
std::string ISBN;
virtual void f(){}
virtual void g(){ //使用virtual,派生类各自定义函数f
std::cout<<"book"<<std::endl;
}
virtual void h(){}
};
class dpar1:public Book{
public:
void g()
{
std::cout<<"ComicBook"<<std::endl;
}
};
class dpar2:public Book{
public:
void g()
{
std::cout<<"ActionBook"<<std::endl;
}
};
int main()
{
dpar1 cb;
dpar2 ab;
//使用基类引用或指针调用虚函数时,发生动态绑定
//根据绑定对象的类型,选择函数的版本
Base* b1 = (Base*)&cb;
Base &b2 = ab;
b1->f();
b2.f();
return 0;
}
输出结果:
如图,父类Base有三个函数,子类Derive重写了父类的g函数(粉色的部分)
在图中,子类Derive的f和h都指向了父类的两个虚函数,只有g指向了自己重写的虚函数
当子类对象的地址赋值给父类对象的指针时,父类对象的vptr已经是子类的vptr了
所以在执行函数g时是查的子类的虚函数表,执行的是子类重写的函数g
类的强制转换
在上面的例子中,ComicBook可以强制转换为Book
其实在类的强制转换时只转换了基类和派生类共同拥有的东西,在上面的例子中就是ISBN和f()
但如果没有共同拥有的东西(不是父类和子类的关系),则不能进行强制转换
class A{
};
class B{
};
int main()
{
A a;B b;
a = (A)b;
}
报错: