C++虚函数入门

参考博客:

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;
}

运行结果:

C++虚函数入门_第1张图片


        至于最后两个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;
}

运行结果:

C++虚函数入门_第2张图片

        可以看到加上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;
}

运行结果如下:

C++虚函数入门_第3张图片

至于纯虚函数的总结:

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函数。



你可能感兴趣的:(C++)