编程小技巧

    返回首页    发表留言
本文作者:李德强
          技巧十五:多态原理
 
 

        今天要跟大家分享的小技巧是关于多态的。多态是面向对象的程序设计最重要的一个特性。多态使得程序变得更加灵活更加抽象。那么多态究竟是什么呢?它在计算机内部到底是如何实现的呢?我们先用一句话来描述:多态就是同一种事物所表现出的多种不同的形态。比如我们在同一个类中编写两个互为重载的方法:

class A
{
	public:
		int max(int a, int b);
		int max(float a, float b, float c);
};

        在使用这个类的对象时,它对外暴露的调用方法都是max(),而它对外所表现出来的形态也是不一样的,给它传入2个参数时,它返回这2个数中较大的那一个,而给它传递3个参数,则返回这3个数中较大的那一个。这就是我们所说的同一个事物(至少对外表现出来的是同一个事物max()函数)在不同情况下(在传入2个参数和3个参数时)所表现出多种不同的形态(计算2个数的较大值和3个数的较大值)。

         类的方法重载是多态中最简单的形式,理解起来也比较简单,接下来我们再来看看类与对象在继承机制中所表现出来的多态形式。我们来看这样的3个类:

        这里,类A中的成员变量int i是protected它会被继承到B类和C类当中,A类中还有一个成员方法virtual void func()这是一个虚函数,它会被B类和C类重写。这3个类的代码实现如下:

class A
{
	public:
		A(int age = 0) :
				i(age)
		{
		}
		virtual ~A()
		{
		}
		virtual void func()
		{
			cout << "My name is A. I'm " << i << " years old." << endl;
		}

	protected:
		int i;
};

class B: public A
{
	public:
		B(int age = 0)
		{
			this->i = age;
		}
		virtual ~B()
		{
		}
		virtual void func()
		{
			cout << "My name is B. I'm " << i << " years old." << endl;
		}
};

class C: public A
{
	public:
		C(int age = 0)
		{
			this->i = age;
		}
		virtual ~C()
		{
		}
		virtual void func()
		{
			cout << "My name is C. I'm " << i << " years old." << endl;
		}
};

int main(void)
{
	A* a0 = new A(20);
	A* a1 = new B(21);
	A* a2 = new C(22);

	a0->func();
	a1->func();
	a2->func();

	delete (a2);
	delete (a1);
	delete (a0);

	return 0;
}

        我们定义了3个A类的对象指针a0、a1、a2。在执行这3个指针所指向的对象的方法func时,它们对外的调用方式都是相同的,然而这3个方法所表现出来的形态却是不同的,这也就是我们所说的多态:

调用的3个方法完全相同
a0->func();
a1->func();
a2->func();
输出结果为:
My name is A. I'm 20 years old.
My name is B. I'm 21 years old.
My name is C. I'm 22 years old.

        事实上虽然a0、a1、a2的类型都是类A的指针类型,但它们在内存中的真正形态是不同的,我们再来仔细看看a0、a1、a2这3个变量定义的代码:

A* a0 = new A(20);
A* a1 = new B(21);
A* a2 = new C(22);

        值得注意的是它们的类型都是A*但是它们在申请内存空间时(也就是使用new修饰符创建对象时)这3个相同类型的指针指向了3个不同类型的对象,分别为A类的对象、B类的对象和C类的对象,它们在内存中实际的内容是这样的:

        也就是说同样的A类的3个指针a0、a1和a2它们所指向的实际对象不同,我们在创建一个对象时(使用new修饰符时)计算机已经在内存中创建了这个对象应该所具有的内存空间,其中也包含了由父类所继承下来的属性,而对外表现同一种类型的对象指针a0、a1和a2在执行对象操作时,实际上就是在执行这3个不同对象,所以会表现出3种不同的形态。

        这里还有一个需要注意的细节:我们使用了virtual修饰符来修饰成员方法void func(),如果不使用virtual修饰符这里的多态机制将不会起作用,因为C++规定使用一个基类的指针来调用子类的成员函数时,如果这个函数是虚函数(被virtual修饰)则调用的函数是子类的函数,如果这个函数不是虚函数则调用的是基类的函数。这也就是C++中的动态联编机制。

 

        今天的小技巧你学会了吗?

 

 

    返回首页    返回顶部
#1楼  点苍双剑  于 2017年08月01日09:05:51 发表
 
Java里面没有设置虚方法,对于Java来说,基类未被指定为final的方法,都是虚函数,都可以被派生类 overwrite。

怪蛙,我的理解对吗?
#2楼  李德强  于 2017年08月02日08:57:11 发表
 
理解的很正确,C++与Java是采用了相反的机制来控制函数的重写:

C++ 规定:指定了virtual的函数才能被重写,没有被指定virtual的函数不能被重写;

Java规定:指定了final的函数不能被重写,没有被指定final的函数才能被重写。
  看不清?点击刷新

 

  Copyright © 2015-2018 问渠网 辽ICP备15013245号