c++学习之多态(虚函数和纯虚函数)

        c++是面向对象语言,面向对象有个重要特点,就是继承和多态。继承之前学过了,就是一种重用类的设计方式。原有的类叫父类,或者基类,继承父类的类叫子类。在设计模式中,我们总是要避免继承,推荐用组合。因为继承会导致类爆炸。类太多了,就太累了。。。哈哈。。。

        说说多态,多态,从我们语言的语法上说,就是通过父类的指针能直接调用子类的方法,在父类的层面,无需了解子类的实现。在我理解来看,其实多态,也是解耦的一种实现方式,因为子类的实现和父类没有关系了,父类总是可以调用到相关的函数。

        但是如果我们父类有完整的一个实现,子类也有完整的实现,肿么办?父类肯定会调用自己的实现方法呀,一会我会讲解这个例子的。为了实现多态,c++语言引用了虚函数的概念。就是说,如果父类的某个函数是虚函数,那么好办,尽管你父类实现了这个虚函数也没关系,我通过指针调用的时候,我也会直接去找子类的相关实现,从而达到多态的效果。这个虚函数就有点像java里面的抽象方法了。但是留意细节哦....c++的虚函数,在父类也是可以实现的。我们来看一个完整的例子。

        1 创建一个BaseClass.h的头文件,对父类BaseClass进行声明。

class BaseClass
{
protected:
	int x;
	int y;
public:
	BaseClass();
	BaseClass(int a,int b);
	void area();
};


        2 创建一个BaseClazz.cpp源文件,对父类BaseClass进行实现。

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

BaseClass::BaseClass()
{
	x = 1;
	y = 2;
}
BaseClass::BaseClass(int a,int b)
{
	x = a;
	y = b;
}
void BaseClass::area()
{
	cout << "baseclass.area() call...." << endl;
cout << x*y <<endl;
}


        3 创建一个DeriveClass1.h的头文件,对子类DeriveClass1进行声明。

#include "BaseClass.h"
class DeriveClass1:public BaseClass
{
private:
	int z;
public:
	void area();
	void setValue(int a);
	DeriveClass1();
};


        4 创建一个DeriveClass1.cpp源文件,对子类进行实现。

#include <iostream>
#include "DeriveClass1.h"

using namespace std;

void DeriveClass1::area()
{
	cout << "DeriveClass1.area() call...." << endl;
cout << x*y*z << endl;
}

void DeriveClass1::setValue(int a)
{
	z = a;
}

DeriveClass1::DeriveClass1()
{
	z = 3;
}


        看到没有,父类子类都有一个area()函数,父类有两个成员变量x,y,子类有三个x,y,z。

        5 创建一个Main.cpp源文件,对以上的代码进行测试。

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

int main()
{

	BaseClass *dc = new DeriveClass1();
	dc->area();
	return 1;
}


        6 好了,这个时候,由于父类和子类都对area函数进行了实现,通过父类的指针来调用area函数,想都不用想,调用的是父类的area函数,尽管将子类的地址赋值给了父类的指针。看结果。

                


        7 这样一来,多态的效果达不到了么,为了达到多态,肿么办?我们将BaseClass.h头文件中的area函数声明为virtual即可,其他地方均不变。看代码,

class BaseClass
{
protected:
	int x;
	int y;
public:
	BaseClass();
	BaseClass(int a,int b);
	virtual void area();
};


        8 这样一来,我们再次运行第六步的测试代码,截图如下:

                

        这就是父类定义了虚函数的结果,尽管父类实现了该虚函数,但是父类的指针在指向子类的情况下,还是会调用子类对应的函数。

        9 这个时候又出现一种情况,如果父类真的是没必要实现这个area函数肿么办??如果你就这样直接不实现,在连接的时候会报错,哈哈。BaseClass.cpp代码如下:

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

BaseClass::BaseClass()
{
	x = 1;
	y = 2;
}
BaseClass::BaseClass(int a,int b)
{
	x = a;
	y = b;
}
void BaseClass::area();


        连接时报错如下:

--------------------Configuration: DeriveDemo - Win32 Debug--------------------
Compiling...
BaseClazz.cpp
Skipping... (no relevant changes detected)
DeriveClass1.cpp
Linking...
BaseClazz.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall BaseClass::area(void)" (?area@BaseClass@@UAEXXZ)
Debug/DeriveDemo.exe : fatal error LNK1120: 1 unresolved externals
执行 link.exe 时出错.

DeriveDemo.exe - 1 error(s), 0 warning(s)


        10 这个时候,纯虚函数就派上用场了,我们只需要将虚函数的定义后面赋值为0,即可。看看BaseClass.h头文件的定义。

class BaseClass
{
protected:
	int x;
	int y;
public:
	BaseClass();
	BaseClass(int a,int b);
	virtual void area()=0;
};


        11 然后编译链接执行。。执行结果如下:



        12 纯虚函数和虚函数有什么区别,我们通过上面的定义可以看出:

                虚函数定义:virtvual void area();

                纯虚函数的定义:virtual void area()=0;

                虚函数,在父类中是必须实现的。

                纯虚函数,在父类中是可以不用实现的。

          熟悉java的同学发现这个有没有一点想抽象类和接口呀。哈哈。。






原文地址:https://www.cnblogs.com/pangblog/p/3243874.html