C++virtual小结

C++virtual小结

被virtual修饰的分为两类:虚函数和虚继承

一:虚函数

当函数被virtual关键字修饰时,该函数被称为虚函数,MSDN上解释如下:

  虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。

例:

#include<iostream>

using namespace std;

class Parent//声明父类
{
public:
    void Function1()
    {
        cout << "执行Parent.Function1\n";
    }
    virtual void Function2()
    {
        cout << "执行Parent.Function2\n";
    }
};

class Child :public Parent//声明子类
{
    void Function1()
    {
        cout << "执行Child.Function1\n";
    }
    void Function2()
    {
        cout << "执行Child.Function2\n";
    }
};
Parent *p;//声明Parent指针
int main()
{
    cout << "输入1,使指针指向Parent类1,输入其他,使指针指向Child子类";
    if ((cin.get()) == '1')
        p = new Parent;
    else
        p = new Child;
    p->Function1();
    p->Function2();
    cin.get();
    cin.get();
    main();//递归main(),实现循环
    return 0;
}

输出结果如下:

输入1,使指针指向Parent类1,输入其他,使指针指向Child子类
输入:1
执行Parent.Function1
执行Parent.Function2

输入1,使指针指向Parent类1,输入其他,使指针指向Child子类
输入:2
执行Parent.Function1
执行Child.Function2

可以看到:
我们声明了一个Parent指针,当输入为1时,指针指向Parent对象,两个函数正确执行。
然而,当输入2时,指针实际却指向了Child类,此时,Function1执行的却是Parent中的。FUnction2正确执行。
为何指向Child的1Function1函数执行错误了呢?

  因为我们是用一个Parent指针调用Function1,虽然这个指针指向的是Child类的对象,但是编译器无法知道这一事实(直到运行时程序才能根据用户的输入判断指针指向的对象),它只能按照调用Parent的函数来理解并编译。所以会出错。

为何指向CHild的Function2函数正确执行了呢?

  注意到:Parent类中的Function2使用了Virtual关键字修饰,也就是说,它是一个虚函数。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并正确调用对象的函数。


二:虚继承

虚继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。

间接或直接地继承同一基类,会在子类中存在多份拷贝。这样将存在两个问题:①浪费存储空间;②存在二义性

①浪费存储空间
例:
如图:类D继承来自类B1、B2,而类B1、B2都继承来自类A,因此会出现如图所示局面(A为非虚基类):
这里写图片描述
为了节省内存空间,可以将B1、B2对A的继承定义为虚继承,那么A就成了虚拟基类。成为下图所示的局面。
这里写图片描述
实现代码如下:

class A{};
class B1 :public virtual A{};
class B2 :public virtual A{};
class D  :public B1,public B2{};

②二义性
仍然以A、B1、B2、D为例,代码如下:

//CForQt.cpp
#include<iostream>
using namespace std;
class A
{
public:
    void f()
    {
        cout << "A.f()" << endl;
    }
private:
    int k;//若基类没有数据成员,则在这里多重继承编译不会出现二义性
};
class B1 :public A{};
class B2 :public A{};
class D  :public B1,public B2{};

int main()
{
    D d;
    d.f();
    cin.get();
    return 0;
}

编译时会出现如下错误:

1>—— 已启动生成: 项目: CForQt, 配置: Debug Win32 ——
1>CForQt.cpp
1>c:\users\31951\source\repos\cforqt\cforqt\cforqt.cpp(25): error C2385: 对“f”的访问不明确
1>c:\users\31951\source\repos\cforqt\cforqt\cforqt.cpp(25): note: 可能是“f”(位于基“A”中)
1>c:\users\31951\source\repos\cforqt\cforqt\cforqt.cpp(25): note: 也可能是“f”(位于基“A”中)
1>已完成生成项目“CForQt.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

从报错中可以看到:编译出错的问题在于对“f”的访问不明确
之所以出现上述问题,是因为编译器在进行编译时,需要确定子类的函数定义,若A:f()是确定的,那么在编译B1、B2是会在编译器的语法树上生成B1::f()和B2::f()等标识,那么在编译D时,由于B1、B2都有一个函数f(),此时编译器将试图生成两个D::f()标识,显然这时就要报错了。(当我们不使用D::f()时,以上标识都不会生成,所以,如果去掉d.f()一句,编译器不会报错)
为了解决上述问题,可以使用虚继承来解决:

#include<iostream>

using namespace std;
class A
{
public:
    void f()
    {
        cout << "A.f()" << endl;
    }
private:
    int k;//若基类没有数据成员,则在这里多重继承编译不会出现二义性
};
class B1 :public virtual A{};
class B2 :public virtual A{};
class D  :public B1,public B2{};

int main()
{
    D d;
    d.f();
    cin.get();
    return 0;
}

此时,编译器输出结果为:

A.f()

参考:
C++中virtual(虚函数)的用法
百度百科:虚继承
转:C++中虚继承的作用及底层实现原理

注:本人能力有限,若博文有错,敬请指正

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值