【C++】虚函数、虚基类、纯虚函数、抽象类到底怎么回事

发布时间:2024年01月18日

目录

1.虚函数

1.1.虚函数使用场景

1.2.virtual,override关键字

2.抽象类

3.虚基类


以下代码在wn10 cygwin gcc 11.4中调试通过?

1.虚函数

定义:虚函数是应在派生类中重新定义的成员函数。
也就是说,虚函数应该在继承的子类中被重写,按照定义举例
class base{
public:
    virtual void abc(){
        cout<<"base.abc"<<endl;
    }
};
class myclass:public base{
public:
    void abc() override{
        cout<<"myclass.abc"<<endl;
    }
};

int main()
{ 
    (new myclass)->abc();
    return 0;
}

运行结果:

myclass.abc

从代码上来看,定义虚函数的方式是对的,在子类中用同名函数重写了子类的函数

这并非虚函数使用的真实意图

因为上面的代码即使不用virtual,或者override定义虚函数,照样可以完成相同效果,举例

class base{
public:
    void abc(){
        cout<<"base.abc"<<endl;
    }
};
class myclass:public base{
public:
    void abc() {
        cout<<"myclass.abc"<<endl;
    }
};

再运行,结果也是一样的

myclass.abc

那么虚函数正确的使用应该什么样子?举例代码如下

class base{
public:
    void abc(){
        cout<<"base.abc"<<endl;
    }
    void ccc(){
        this->abc();
    }
};
class myclass:public base{
public:
    void abc(){
        cout<<"myclass.abc"<<endl;
    }
};
int main(){ 
    (new myclass)->ccc();
    return 0;
}

以上代码输出

base.abc

从代码上看,这个结果是正确的,调用逻辑也对。把上面的代码改一改

class base{
public:
    virtual void abc(){
        cout<<"base.abc"<<endl;
    }
    void ccc(){
        this->abc();
    }
};
class myclass:public base{
public:
    void abc() override{
        cout<<"myclass.abc"<<endl;
    }
};

然后在运行,这次输出如下

myclass.abc
从运行中可以发现,父类ccc中调用了abc,这个abc并非base中的abc,而是myclass中的abc,也就是说,子类的abc覆盖了父类的abc,应为base中的abc被定义为虚函数,便被子类的abc覆盖了。这个才是虚函数的真实意图

1.1.虚函数使用场景

一个简单的场景,假设有两个类,继承了同样一个基类,调用基类的abc,但按照业务需求,abc需要实现不同逻辑,这时,虚函数就有用了,举例如下
class base{
public:
    virtual void abc(){
        cout<<"base.abc"<<endl;
    }
    /*其他代码*/
};
class myclass:public base{
public:
    void abc() override{
        cout<<"myclass.abc"<<endl;
    }
};

class yourclass:public base{
public:
    void abc() override{
        cout<<"yourclass.abc"<<endl;
    }
};
int main(){
    base *b1  = nullptr;
    if(/**/){
        b1 = new myclass();
    }else{
        b1 = new yourclass();
    }
    b1->abc();
    return 0;
}

以上代码如果调用了if内,则使用了myclass 的abc,否则就使用了yourclass的abc

这样,通过业务需求来调用不同的abc,就实现了不同逻辑的调用

1.2.virtual,override关键字

从以上介绍可以了解到,定义虚函数使用的方法,使用两个关键字,如下:

virtual:关键字声明一个虚拟函数或一个虚拟基类

override:说明一个函数是重写函数,实际上它并非关键字,仅是一个说明符,如下代码

    int override = 1;

运行会发现,这句话并不报错,说明override并不是关键字

同时,在重写virtual定义的虚函数时,override可有可无,只要定义方式对,就可以

但是如果定义方式有差别,使用override 可以检测定义是否正确,举例如下

class base{
public:
    virtual void abc() const{
        cout<<"base"<<endl;
    };
};
class myclass:public base{
public:
   void abc(){
     cout<<"myclass"<<endl;
   }
};

基类中的abc定义了const,myclass中的abc没有写const,也就是没有完整定义,这样,是无法覆盖的基类的

如果这个时候myclsss中的abc加上了override,则会提示错误

error: 'void myclass::abc()' marked 'override', but does not override

如果没有override,但在子类中完整定义后也没有问题了,如下写法

class myclass:public base{
public:
   void abc() const{
     cout<<"myclass"<<endl;
   }
};

2.抽象类

定义一个抽象类,并不使用任何关键字,它只是个概念,只要是类中有一个格式如下的函数定义,它就是抽象类

virtual void abc() = 0;

这样的函数,被成为纯虚函数,它是一个没有函数内容的函数,仅用来被重写
抽象类不能被实例化,举例如下

class base{
public:
    virtual void abc() = 0;
};
int main(){
    base b;
    return 0;
}
Variable type 'base' is an abstract class

抽象类只能用于被继承,且虚函数必须被子类重写,举例如下

class base{
public:
    virtual void abc() = 0;
};
class myclass:public base{
public:
   void abc(){
     cout<<"myclass"<<endl;
   }
};

3.虚基类

在C++是允许多继承的,但是多继承的会出现一个问题,就是构造函数调用的问题,举例如下

class base{
public:
    base(){
         cout<<"base"<<endl;
    };
};
class class_1:public base{
public:
    class_1(){
        cout<<"class_1"<<endl;
    }
};
class class_2:public base{
public:
    class_2(){
        cout<<"class_2"<<endl;
    }
};
class myclass:public class_2,public class_1{};
int main(){
    myclass m;
    return 0;
}

以上代码,class_1,class_2继承了base,myclass又同时继承了class_1,class_2

运行后,输出如下

base
class_2
base
class_1

可以看到base的构造函数被调用了两次,这个结果,可能是需要的,但大多数情况下可能你不需要,如果不需要,这个时候,就需要用虚基类来解决这个问题

修改以上代码,给class_1,class_2继承base时加上virtual关键字,如下,其他代码不变

class class_1:virtual public base{
public:
    class_1(){
        cout<<"class_1"<<endl;
    }
};
class class_2:virtual public base{
public:
    class_2(){
        cout<<"class_2"<<endl;
    }
};

然后运行

base
class_2
class_1

可以看到,base构造函数就调用了一次。这就是虚基类的作用。原本在没有虚基类的情况下,base会依据被继承的情况,出现多个,但使用了虚基类后,基类就只会保留一个副本

文章来源:https://blog.csdn.net/weixin_44305576/article/details/135629321
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。