虚函数和纯虚函数和析构函数

 

 

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

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\n");
14     }
15 };
16 
17 class DerivedB:public Base
18 {
19 public:
20     DerivedB(){};
21     void Draw()
22     {
23         printf("B draw\n");
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()\n";
    }
};
 
class B:public A
{
public:
    void show()
    {
        cout<<"in B::show()\n";
    }
};
 
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()\n";
    }
    void callfunc()
    {
        show();
    }
};
 
class B:public A
{
public:
    void show()
    {
        cout<<"in B::show()\n";
    }
};
 
int main()
{
    B b;
    b.callfunc();    //调用的是A::callfunc(),,但在A::callfunc()调用的是B::show()
            //这就是一个虚调用
    A a;
    a.callfunc();    //这里调用的是A::show()
    return 0;
}
View Code

输出:

 

posted @ 2019-04-13 17:43  Brickert  Views(1479)  Comments(0Edit  收藏  举报