彻底搞懂C++多态虚函数等问题

1.继承和覆写

子类继承父类,子类可以覆写基类的函数。当然,直接生成一个子类的对象,用子类的指针操作是没有问题的。调用的函数是子类的函数,即覆写后的。

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
	Base(void)
	{
		cout<<"Base is created"<<endl;
	}
	virtual ~Base(void)
	{
		cout<<"Base destroyed!"<<endl;
	}
	void func()
	{
		cout<<"Base function is called"<<endl;
	}
};

class Demo : public Base
{
public:
	Demo(void)
	{
		cout<<"Demo is created"<<endl;
	}
	~Demo(void)
	{
		cout<<"Demo destroyed!"<<endl;
	}

	void func()
	{
		cout<<"Demo function is called"<<endl;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	
	Demo* demo = new Demo();
	demo->func();
	
	int a;
	cin>>a;
	return 0;
}


结果:

Base is created
Demo is created
Demo function is called

但是,如果要用父类的指针调用子类的对象呢?还是会调用覆写后的函数吗?

答案是不会。。。

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
	Base(void)
	{
		cout<<"Base is created"<<endl;
	}
	virtual ~Base(void)
	{
		cout<<"Base destroyed!"<<endl;
	}
	void func()
	{
		cout<<"Base function is called"<<endl;
	}
};

class Demo : public Base
{
public:
	Demo(void)
	{
		cout<<"Demo is created"<<endl;
	}
	~Demo(void)
	{
		cout<<"Demo destroyed!"<<endl;
	}

	void func()
	{
		cout<<"Demo function is called"<<endl;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	Base* base = new Demo();
	base->func();
	//Demo* demo = new Demo();
	//demo->func();
	//delete base;
	int a;
	cin>>a;
	return 0;
}


Base is created
Demo is created
Base function is called

那么要肿么办呢?

下面就是多态的方法啦。

2.关于多态

多态实际上就是用基类的指针来操作子类的对象。但是上面覆写了之后,却调用的还是父类的函数。要解决这个问题就要加一个关键字,virtual。

就是这一个关键字就改变了程序运行的结果。

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
	Base(void)
	{
		cout<<"Base is created"<<endl;
	}
	virtual ~Base(void)
	{
		cout<<"Base destroyed!"<<endl;
	}
	void virtual func()
	{
		cout<<"Base function is called"<<endl;
	}
};

class Demo : public Base
{
public:
	Demo(void)
	{
		cout<<"Demo is created"<<endl;
	}
	~Demo(void)
	{
		cout<<"Demo destroyed!"<<endl;
	}

	void func()
	{
		cout<<"Demo function is called"<<endl;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	Base* base = new Demo();
	base->func();
	//Demo* demo = new Demo();
	//demo->func();
	//delete base;
	int a;
	cin>>a;
	return 0;
}

在要被覆写的基类函数前面加上virtual关键字就可以使父类指针调用子类对象的重写的函数。

结果:

Base is created
Demo is created
Demo function is called


但是还有一种情况,就是即使覆写了父类的函数,但仍然需要父类的函数的功能,这要肿么办呢?

最开始我的想法是从父类的Ctrl+c 然后Ctrl+v过来。后来一想,这个也忒麻烦了吧,而且不利于代码的维护。还好,有这样一个简单的方法。


在子类覆写的函数中,加上这句

Base::func();

即调用了父类的函数,然后再加上子类特有的功能即可。



// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
	Base(void)
	{
		cout<<"Base is created"<<endl;
	}
	virtual ~Base(void)
	{
		cout<<"Base destroyed!"<<endl;
	}
	void virtual func()
	{
		cout<<"Base function is called"<<endl;
	}
};

class Demo : public Base
{
public:
	Demo(void)
	{
		cout<<"Demo is created"<<endl;
	}
	~Demo(void)
	{
		cout<<"Demo destroyed!"<<endl;
	}

	void func()
	{
		Base::func();
		cout<<"Demo function is called"<<endl;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	Base* base = new Demo();
	base->func();
	//Demo* demo = new Demo();
	//demo->func();
	//delete base;
	int a;
	cin>>a;
	return 0;
}

Base is created
Demo is created
Base function is called
Demo function is called

这样就能既使用子类的特有功能,又调用了父类的功能。


3..虚析构函数

如果是基类,没有定义为虚析构函数的话,用基类指针操作子类,不会调用子类对象的析构函数,会导致只释放了基类部分的资源,而定义在子类部分的资源没被释放,造成内存泄露。

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
	Base(void)
	{
		cout<<"Base is created"<<endl;
	}
	virtual ~Base(void)
	{
		cout<<"Base destroyed!"<<endl;
	}
};

class Demo : public Base
{
public:
	Demo(void)
	{
		cout<<"Demo is created"<<endl;
	}
	~Demo(void)
	{
		cout<<"Demo destroyed!"<<endl;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	Base* base = new Demo();
	delete base;
	int a;
	cin>>a;
	return 0;
}
运行结果:

Base is created
Demo is created
Demo destroyed!
Base destroyed!

子类对象构造时,会先调用父类的构造函数,然后调用子类的构造函数,初始化子类的特有部分。析构时,顺序相反,先调用子类的析构函数,再调用父类的析构函数。

构造函数和析构函数中都是默认调用父类的函数的。不用像上面那样要额外加上调用父类函数的句子。




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。