参考博客:
1. 虚函数与纯虚函数
2. 虚函数和纯虚函数的作用与区别
3. C++一些注意点之友元函数、虚函数以及const和volatile对象
case 1 :没有用虚函数的情况:
//virtual.h
#include
#include
using namespace std;
class Animal
{
private:
string m_name;
public:
Animal(string n):m_name(n){};
~Animal(){cout<<"destructor of Animal::"<
//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::"<
virtual void bark(){cout<<"Bark~Dog!"<
再加上一个Cat,没有复写bark():
class Cat:public Animal
{
public:
Cat(string n):Animal(n){};
//virtual void bark(){cout<<"Bark~Cat!"<
然后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
#include
using namespace std;
class Animal
{
private:
string m_name;
public:
Animal(string n):m_name(n){};
~Animal(){cout<<"destructor of Animal::"<
#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
#include
using namespace std;
class Animal
{
private:
string m_name;
public:
Animal(string n):m_name(n){};
~Animal(){cout<<"destructor of Animal::"<
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函数。