C++虚函数入门

来源:互联网 发布:知乎日报启动画面 编辑:程序博客网 时间:2024/06/07 01:56

参考博客:

1. 虚函数与纯虚函数

2. 虚函数和纯虚函数的作用与区别

3. C++一些注意点之友元函数、虚函数以及const和volatile对象


case 1 :没有用虚函数的情况:

//virtual.h#include <iostream>#include <string>using namespace std;class Animal{private:string m_name;public:Animal(string n):m_name(n){};~Animal(){cout<<"destructor of Animal::"<<getName()<<endl;}string getName(){return m_name;}void setName(string n){m_name = n;}void bark(){cout<<"Bark~Animal::"<<getName()<<endl;}};class Dog:public Animal{public:Dog(string n):Animal(n){};void bark(){cout<<"Bark~Dog!"<<getName()<<endl;}~Dog(){cout<<"destructor of Dog::"<<getName()<<endl;}};

//virtual.cpp#include "virtualFunc.h"int main(){Animal p("Alex");Dog d("Ben");p.bark();d.bark();Animal* p_a = NULL;p_a = &p;p_a->bark();p_a = &d;p_a->bark();return 0;}

运行结果:



        至于最后两个bark都是调用基类Animal::bark(),是因为p_a是定义为Animal的指针,调用bark()时,bark()不是虚函数,所以直接调基类Animal::bark()。


case 2:下面加上虚函数来看看。只要在bark函数前加上virtual即可:

virtual void bark(){cout<<"Bark~Animal::"<<getName()<<endl;}
virtual void bark(){cout<<"Bark~Dog!"<<getName()<<endl;}

再加上一个Cat,没有复写bark():

class Cat:public Animal{public:Cat(string n):Animal(n){};//virtual void bark(){cout<<"Bark~Cat!"<<getName()<<endl;}~Cat(){cout<<"destructor of Cat::"<<getName()<<endl;}};
        然后main函数里,将p_a指向Cat实例,并调用bark():

#include "virtualFunc.h"int main(){Animal p("Alex");Dog d("Ben");Cat c("Cindy");p.bark();d.bark();Animal* p_a = NULL;p_a = &p;p_a->bark();p_a = &d;p_a->bark();p_a = &c;p_a->bark();return 0;}

运行结果:



        可以看到加上virtual的效果了。p_a调用子类Dog::bark()!因为每种动物的bark()都会不同,所以在基类Animal里将bark定义为virtual,子类就可以复写。当子类没有bark()的时候,才去调基类的bark()。


case 3:如果改成纯虚函数

基类Animal里将bark改成如下:

virtual void bark()=0;
运行,发现报错!

error C2259:'Animal‘:cannot instanitate abstract class

error C2259:'Cat‘:cannot instanitate abstract class

        这就是纯虚函数的一个要注意的地方:有纯虚函数的类,称为虚基类,是不可以实例化!只可以声明指针。

        Cat有问题是因为,Cat继承Animal,Animal的bark()是虚基类,在子类里必须要实现,所以在Cat里要将bark()实现,否则Cat也不能实例化,因为它和基类一样,也是抽象类!

那么改成如下:

#include <iostream>#include <string>using namespace std;class Animal{private:string m_name;public:Animal(string n):m_name(n){};~Animal(){cout<<"destructor of Animal::"<<getName()<<endl;}string getName(){return m_name;}void setName(string n){m_name = n;}//virtual void bark(){cout<<"Bark~Animal::"<<getName()<<endl;};virtual void bark()=0;};class Dog:public Animal{public:Dog(string n):Animal(n){};virtual void bark(){cout<<"Bark~Dog!"<<getName()<<endl;}~Dog(){cout<<"destructor of Dog::"<<getName()<<endl;}};class Cat:public Animal{public:Cat(string n):Animal(n){};virtual void bark(){cout<<"Bark~Cat!"<<getName()<<endl;}~Cat(){cout<<"destructor of Cat::"<<getName()<<endl;}};

#include "virtualFunc.h"int main(){Animal* p;Dog d("Ben");Cat c("Cindy");p = &d;p->bark();d.bark();c.bark();p = &c;p->bark();return 0;}

运行结果如下:


至于纯虚函数的总结:

1. 只定义,并不去实现。给出函数的接口,在子类里去实现。


但是通常我们可以看到函数后有加上const=0,下面引出const函数。

还是原先的例子,改成如下:

#include <iostream>#include <string>using namespace std;class Animal{private:string m_name;public:Animal(string n):m_name(n){};~Animal(){cout<<"destructor of Animal::"<<getName()<<endl;}string getName(){return m_name;}void setName(string n){m_name = n;}virtual void bark()const{cout<<"Bark~Animal::"<<getName()<<endl;};//加上const修饰};class Dog:public Animal{public:Dog(string n):Animal(n){};virtual void bark() {cout<<"Bark~Dog!"<<getName()<<endl;}~Dog(){cout<<"destructor of Dog::"<<endl;}};class Cat:public Animal{public:Cat(string n):Animal(n){};virtual void bark() {cout<<"Bark~Cat!"<<getName()<<endl;}~Cat(){cout<<"destructor of Cat::"<<endl;}};

运行报错!

error C2662: 'Animal::getName' : cannot convert 'this' pointer from 'const Animal' to 'Animal &'

解决:是因为const函数bark()里调了getName(),如果把它删了,则正常了。可是如果我们想要调它怎么办呢?把getName()也改成const函数即可!const修饰的对象只能访问const修饰的函数成员,不能访问其他成员函数。getName()之前是非const函数,所以bark()不能访问。

        至于为什么要用const修饰?如果我们用const 修饰getName()后,在getName()里去修改m_name,编译器会报错!不用const修饰,修改则没事。所以const用于告诉编译器,这个函数里不允许修改任何类的数据。同理,它只能调其他const函数成员,是因为如果掉非const函数的话,有可能会修改类的数据。


        到这里,我们知道在函数后加上const=0是什么意思了。const修饰函数为常函数,=0表明它是纯虚函数(当然要有virtual)。它必须:

1. 基类只声明,不实现。

2. 派生类里的纯虚函数之能调用const函数。