编程小技巧:多态原理

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

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

}

protected:

int i;

};

class B: public A

{

public:

B(int age = 0)

{

this->i = age;

}

virtual ~B()

{

}

virtual void func()

{

cout

}

};

class C: public A

{

public:

C(int age = 0)

{

this->i = age;

}

virtual ~C()

{

}

virtual void func()

{

cout

}

};

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++中的动态联编机制。C++与Java是采用了相反的机制来控制函数的重写:

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

Java规定:指定了final的函数不能被重写,没有被指定final的函数才能被重写。

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

本文来自企鹅号 - 全球大搜罗媒体

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

Python那些事——Python之函数式编程!

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有...

1977
来自专栏木可大大

面向对象的演进过程

我们知道 程序 = 数据结构 + 算法,其中数据结构包括数组、栈、队列、链表、树以及图等,而算法是包含顺序、循环、分支三种逻辑结构的代码,为了使算法能够到处复用...

4379
来自专栏deepcc

Js apply() call()使用详解

3556
来自专栏史上最简单的Spring Cloud教程

深入浅出工厂设计模式

工厂设计模式 一.什么是工厂设计模式 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。因为工厂模式就相当于创建实例对象的new,...

2155
来自专栏web前端教室

常用技巧之JS判断数组中某元素出现次数

现在前端开发经常需要从api中获取返回的数组, 也许是array,也许是json, 不管是什么,都需要对返回的数据进行再处理, 其中一个重要且经常用到的操作, ...

4668
来自专栏老九学堂

一分钟掌握C语言结构体常见方法

把结构体名称去掉,这样更简洁,不过也不能定义其他同结构体变量了——至少我现在没掌握这种方法。

1202
来自专栏Java学习网

Java变量类型转换规则与注意事项

Java变量类型对于每个从事Java开发工作的人员来说再熟悉不过了,正如你所知,Java的数据类型分为三大类:布尔型、字符型和数值型,而其中数值型又分为整型和浮...

2446
来自专栏about云

spark中 map和reduce理解及与hadoop的map、reduce区别

问题导读 1.你认为map函数可以做哪些事情? 2.hadoop中map函数与Scala中函数功能是否一致? 3.Scala中reduce函数与hadoop中...

2888
来自专栏Java与Android技术栈

借助Java 8实现柯里化借助Java 8实现柯里化柯里化的好处总结

在函数式编程中,函数的概念跟数学中函数的概念是一样的,类似于“映射”。高阶函数和柯里化是函数式编程的特性。

1302
来自专栏Python爱好者

Java基础笔记06

1468

扫码关注云+社区

领取腾讯云代金券