C++ Primer学习笔记(13)——封装、继承、多态

来源:互联网 发布:科目四考试软件 编辑:程序博客网 时间:2024/05/21 07:48

C++ 是一种典型的面向对象的编程语言,其最显著地特点是封装、继承和多态。充分理解封装、继承、多态是如何实现的,学好C++就不是难事了。

这里写图片描述

1.封装

封装是将多个细节元素组合在一起,隐藏起来,成为一个类。外部无法了解类内部是如何实现的,只需要考虑它的接口。
例如:将公司员工的姓名、年龄、工号等信息放在类的私有部分。

封装的好处:
避免类内部出现无意的、可能破坏对象状态的用户级错误。
随时间推移可以根据需求改变或缺陷报告来完善类实现,而无需改变用户级代码。


2.继承

该部分转自ruyue_ruyue的博客

什么是继承?

继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。其继承的过程,就是从一般到特殊的过程。
通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承的实现方式?
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
1. 实现继承是指使用基类的属性和方法而无需额外编码的能力;
2. 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
3. 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。


3. 多态

C++多态分为两种:静多态 和 动多态。静多态发生在编译期,动多态发生在运行期。

~ 静多态 ~

静多态可以通过模板和函数重载来实现(之所以说c++中的多态,主要还是因为模板这个东西)
1)函数模板

template T max(const T& lsh, const T& rsh){    return (lsh > rsh) ? lsh : rsh;}

这是一个模板,返回任意类型对象的最大值,前提是该类型能够使用 > 运算符进行比较,并且返回值是bool 型。
模板的使用:

int a = 2, b = 3;cout << max(a, b) << endl;  float c = 2.0 , d = 3.0;cout << max(c, d) << endl;

输出结果为:

33.0

由于模板的实例化发生在编译期,故上述绑定是发生在编译期的,即在编译时编译器发现你调用 max(a, b)时自动生成如下函数(将模板中的 T 全部用 int 替换):

int max(const int& lsh, const int& rsh){    return (lsh > rsh) ? lsh:rsh;}

同样地,当编译器发现你调用 max(c, d) 时,自动将模板中的 T 用 float 替换了。

2)函数重载
如果两个函数名相同,其参数类型或参数个数不完全相同,则成为函数重载。函数调用的时候,根据实参的类型和个数确定调用哪个函数。
(如果两个函数是同一个函数,必须是其函数名、参数类型、参数个数、函数体完全相同。)

举个例子:

int max (int a , int b){ // 函数1    return (a > b) ? a:b; }int max (int a , int b , int c){ // 函数2    return max(max(a,b),c); }float max (float a ,float b){ // 函数3    return (a > b) ? a:b;}

使用:

int a = 2, b = 3, c = 4;float a1 = 2.0 , b1 = 3.0;cout << max(a,b) << endl;   // 函数1cout << max(a,b,c) <<endl;  // 函数2cout << max(a1,b1) <<endl;  // 函数3

输出结果为:

343.0

确定具体调用哪个函数的过程也发生在编译期。max(a,b) ,编译器发现只有两个int 类型的参数,就调用函数1 ;max(a,b,c) ,有三个int 型参数,调用函数2;max(a1,b1),有两个float 型参数,于是调用函数3。该过程称作函数匹配。

~ 动多态 ~

动多态是通过继承、虚函数(virtual)、指针来实现的,关键是虚函数的使用。关于虚函数的实现机制,是C++面试中几乎总会被问到的,具体实现机制我将在下一篇文章中详细阐述。
下面讲述多态的实现:

class A {public:    virtual void func() const    {        cout << "A :: func()"  << endl;    }};class B : public A {public:    virtual void func() const    {        cout << "B :: func()" << endl;    }};

使用:

A *a = B();     // 定义 A 类型的指针,指向 B 对象A -> func();    // 指针 A 指向func()函数

在编译期是不调用任何函数的,编译器编译到 a->func()时只是检查有没有语法错误,并不知道调用的是 A 类还是 B 类的 func() , 由于 a 是一个指向 B 对象的指针,所以a 只知道它指向的是一个 A 类型(或者能转换成A类型)的对象。由于是公有继承,说明B是一种A。
在运行期,a 要调用a所指向对象的 func() 函数,就对它指向的对象下达调用func() 的命令,结果a 所指向的是一个 B 对象,所以就调用了B类型的func()函数,所以输出地是 B::func()。

总结:
在编译期的行为是静多态,有模板和函数重载;
在运行期的行为是动多态,通过虚函数来实现。

0 0
原创粉丝点击