zoukankan      html  css  js  c++  java
  • 虚函数和纯虚函数和析构函数

    记录了学习虚函数与纯虚函数中有一些疑问,以及平常可能不注意的地方。

    Q0:虚函数是怎么实现的?

    0:简单的说,是通过虚函数表实现的。如果一个类中含有虚函数,则系统会为这个类分配一个指针成员指向一张虚函数表(vtbl),表中每一项指向一个虚函数的地址,实现上就是一个函数指针的数组。

    Q1:基类函数加上virtual关键字,派生类不加,那么派生类的同名函数是虚函数吗?

    1.C++继承中,如果基类定义了一个虚函数,那么派生类中的同名函数不用加virtual(也可以加)关键字,也可以使该函数为虚函数。因为派生类继承了基类的属性,所以派生类中同名函数被视为与基类有相同属性的函数。

    如果基类为纯虚函数,那么派生类中也不用声明virtual,但是必须实现纯虚函数。

    纯虚函数基类中定义:virtual fun() = 0;派生类可以有不同的实现,即可以称在基类中提供了该函数的接口。

    Q2:那为什么需要这个接口呢,比如我直接可以在子类中分别写同名函数,然后一一实现。

    2.看到的一个很形象例子:比如基类的接口类似与电脑的USB接口,不同的设备都可以插入访问数据,U盘、鼠标、键盘;如果分别实现的话,那么就需要在电脑上加三个不同类型的接口,而纯虚函数的作用,则只需要这一个接口来对不同的需求进行操作,非常方便。

    Q3:构造函数可以是虚函数吗?析构函数呢?

    3:构造函数不能是虚函数,因为要构造一个对象,就必须知道构造了什么;析构函数一般定义为虚函数,称为虚析构函数(指向派生类对象的基类指针被delete时,会调用派生类析构函数、基类的析构函数,否则只会调用基类的析构函数),可以定义为纯虚函数。

    (可以参考 http://www.cnblogs.com/chio/archive/2007/09/10/888260.html)
    讲了平常不会注意的地方:
    纯虚函数也可以定义函数体;
    6) 析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。 
    7) 非纯的虚函数必须有定义体,不然是一个错误。

    虚函数的作用:(例子) 

     1 class Base
     2 {
     3 public:
     4     virtual void Draw(){};
     5 };
     6 
     7 class DerivedA:public Base
     8 {
     9 public:
    10     DerivedA(){};
    11     void Draw()
    12     {
    13         printf("A draw
    ");
    14     }
    15 };
    16 
    17 class DerivedB:public Base
    18 {
    19 public:
    20     DerivedB(){};
    21     void Draw()
    22     {
    23         printf("B draw
    ");
    24     }
    25 };    

    如果要DerivedA或者DerivedB调用Draw(),可以分别new它们的对象,然后调用Draw(),但是如果还有其他的派生类,则需要定义并new很多个对象;

    那么,虚函数的作用:

    Base* base = new DerivedA; 或者 Base* base = new DerivedB;

    base->Draw(); //按照new的对象,对应调用派生类的Draw()函数,只需要定义一个base指针即可。

    纯虚函数的作用:(例子)

    class Vehicle
    {
    public:
        virtual void Print()=0//纯虚函数的定义
    };
    
    class Car:public Vehicle
    {
    public:
        virtual void Print(){cout<<"Car"<<endl;};
    };
    
    class Bike:public Vehicle
    {
    public:
        virtual void Print(){cout<<"Bike"<<endl;};
    };
    
    void main()
    {
        Car c;
        Bike b;
        b.Print();  //输出Bike
        c.Print();  //输出Car
    }

    这里Vehicle为抽象类,也叫纯虚类;定义了print()=0;派生类Car和Bike实现了print()函数,按照对象调用各自的print()函数。

    Q4:如何通过指向派生类对象的指针或引用调用基类对象的虚函数?(参考https://blog.csdn.net/chijianxingfeng/article/details/8870387)

    例一:

    #include <iostream>
    using namespace std;
     
    class A
    {
    public:
        virtual void show()
        {
            cout<<"in A::show()
    ";
        }
    };
     
    class B:public A
    {
    public:
        void show()
        {
            cout<<"in B::show()
    ";
        }
    };
     
    int main()
    {
        A a;
        //通过派生类对象的引用pb 实现了调用基类中虚函数show(),,
        //如果把 A中show() 前面的virtual去掉, 则调用的就是B 中的show()
        B &pb = static_cast<B&>(a);
        pb.show();    //调用的是基类 A的 show();
        return 0;
    }
    View Code

    输出:

    例二:

    #include <iostream>
    using namespace std;
     
    class A
    {
    public:
        virtual void show()
        {
            cout<<"in A::show()
    ";
        }
        void callfunc()
        {
            show();
        }
    };
     
    class B:public A
    {
    public:
        void show()
        {
            cout<<"in B::show()
    ";
        }
    };
     
    int main()
    {
        B b;
        b.callfunc();    //调用的是A::callfunc(),,但在A::callfunc()调用的是B::show()
                //这就是一个虚调用
        A a;
        a.callfunc();    //这里调用的是A::show()
        return 0;
    }
    View Code

    输出:

  • 相关阅读:
    cnblog项目--20190309
    django js引入失效问题
    Python老男孩 day16 函数(六) 匿名函数
    Python老男孩 day16 函数(五) 函数的作用域
    Python老男孩 day15 函数(四) 递归
    Python老男孩 day15 函数(三) 前向引用之'函数即变量'
    Python老男孩 day15 函数(二) 局部变量与全局变量
    Python老男孩 day14 函数(一)
    Python老男孩 day14 字符串格式化
    Python老男孩 day14 集合
  • 原文地址:https://www.cnblogs.com/Brickert/p/10702101.html
Copyright © 2011-2022 走看看