虚函数与虚继承

虚函数多态:

  1. 至少有两个有继承关系的类.
  2. 基类与派生类中有同名同参的函数,且基类函数被virtual所修饰(覆盖).
  3. 必须通过基类的引用或指针调用该函数.
    .声明语法:
    virtual 虚函数类型函数名(形参表)
    多态性:
    在C++中,多态类型是指声明或者继承了至少一个虚函数的类型,反之则为非多态类型。
    对于非多态类型:
 struct A
        {
            void foo() {}
        };  
        A a;
        std::cout << typeid(a).name();  // 可以在编译时确定a的类型为A
        a.foo();     // 可以在编译时确定A::foo在内存中的地址
        sizeof(a);   // 尽管A为空,但由于需要在内存中确定a的地址,因此A类型对象所占空间为1个字节

它的类型可以确定。
而对于的多态类型:

struct A
{
    virtual void foo() {} // 声明虚函数
};

struct B : public A
{
    // 隐式继承了虚函数
};

...

B b{};
A& a_rb = b; // 将b绑定到A的左值引用a_rb上

typeid(decltype(a_rb)).name(); // decltype产生的是编译时即可确定的声明类型,因此为A
typeid(a_rb).name();  // 由于a_rb是多态类型的glvalue,typeid在运行时计算,因此为B

a_rb.foo();  // 这里调用的是B中的foo,其函数地址是运行时确定的
sizeof(b);   // 这里的sizeof是编译器决定的,通常为8 (64位)

多态类型,一些信息必须延迟到运行时才可以确定,例如它的实际类型、所调用的虚函数的地址等。下面的这个例子中,类型B继承了声明有虚函数的类型A,因此A和B都是多态类型。
实现原理:
虚表:是一个存放本类所有虚函数入口地址的静态指针数组;
重载、隐藏、覆盖:
重载:在同一作用域中,同名不同参.
覆盖:分别位于基类和派生类中,同名同参,且基类函数被virtual所修饰.
隐藏: 分别位于基类和派生类中
a.同名同参,基类函数没有被virtual所修饰
b.同名不同参.

如果基类函数为虚函数,则派生类中同名同参函数既是没有virtual修饰,也是虚函数。

规定:

  1. 构造函数不能为虚函数.

  2. 内联函数不能为虚函数.

  3. 静态成员函数不能为虚函数.

  4. 析构函数可以为虚函数,而却通常是虚函数.

    单继承
    对于每一个多态类型,其所有的虚函数的地址都以一个表格的方式存放在一起,每个函数的偏移量在基类型和导出类型中均相同,这使得虚函数相对于表格首地址的偏移量在可以在编译时确定。虚函数表格的首地址储存在每一个对象之中,称为虚(表)指针(vptr)或者虚函数指针(vfptr),这个虚指针始终位于对象的起始地址。使用多态类型的引用或指针调用虚函数时,首先通过虚指针和偏移量计算出虚函数的地址,然后进行调用。
    例如:

struct A
{
public:
	virtual void f0() {
		cout << "f0()" << endl;
	}
	virtual void f1() {
		cout << "f1()" << endl;
	}
};

struct B : public A
{
public:
	void f0(){
		cout << "B::f0()" << endl;
	}; 
};
int main()
{
	B a;
	a.f0();
	a.f1();
	return 0;
}

在这里插入图片描述
多继承:

class Base
{
public://基类中有虚继承,则派生类中会自动加一个virtual
	virtual void f1() { cout << "Base::f1()" << endl;}
	virtual void f2() { cout << "Base::f2()" << endl; }
	void f3() { cout << "Base::f3()" << endl; }
	void f4() { cout << "Base::f4()" << endl; }

	virtual void f5() { cout << "Base::f5()" << endl; }
	void f6() { cout << "Base::f6()" << endl; }

	virtual ~Base() { cout << "~Base()" << endl; }

	int i;
};


class D :public Base
{
public:
	virtual void f1() { cout << "D::f1()" << endl; }
	void f2() { cout << "D::f2()" << endl; }//base中有virtual,则void f2()默认为virtual void f2()
	virtual void f3() { cout << "D::f3()" << endl; }
	void f4() { cout << "D::f4()" << endl; }
	virtual void f7() { cout << "B::f7()" << endl; }//如果派生类中有virtual void f7() ,并且基类没有编译会出错
	void f8() { cout << "B::f8()" << endl; }
	~D() { cout << "~D()" << endl; }
	int j;
};

与单链继承不同,由于A和B完全独立,它们的虚函数没有顺序关系,即f0和f1有着相同对虚表起始位置的偏移量,不可以顺序排布。 并且A和B中的成员变量也是无关的,因此基类间也不具有包含关系。这使得A和B在C中必须要处于两个不相交的区域中,同时需要有两个虚指针分别对它们虚函数进行索引。

虚表:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值