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

    输出:

  • 相关阅读:
    node.js的request模块
    PHP实现一个简单url路由功能
    关于seajs
    CodeIgniter集成Smarty
    node.js批量修改图片名
    Node.js创建目录实例
    Bootstrap的表单设计器
    onbeforeunload事件被a链接触发的问题
    Socket.IO + Express实现的跨浏览器、子域的聊天室
    NodeJS获取命令行后面的参数
  • 原文地址:https://www.cnblogs.com/Brickert/p/10702101.html
Copyright © 2011-2022 走看看