C++ 是一种典型的面向对象的编程语言,其最显著地特点是封装、继承和多态。充分理解封装、继承、多态是如何实现的,学好C++就不是难事了。
封装是将多个细节元素组合在一起,隐藏起来,成为一个类。外部无法了解类内部是如何实现的,只需要考虑它的接口。
例如:将公司员工的姓名、年龄、工号等信息放在类的私有部分。
封装的好处:
避免类内部出现无意的、可能破坏对象状态的用户级错误。
随时间推移可以根据需求改变或缺陷报告来完善类实现,而无需改变用户级代码。
该部分转自ruyue_ruyue的博客
什么是继承?
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。其继承的过程,就是从一般到特殊的过程。
通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承的实现方式?
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
1. 实现继承是指使用基类的属性和方法而无需额外编码的能力;
2. 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
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;
输出结果为:
3
3.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; // 函数1
cout << max(a,b,c) <<endl; // 函数2
cout << max(a1,b1) <<endl; // 函数3
输出结果为:
3
4
3.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()。
总结:
在编译期的行为是静多态,有模板和函数重载;
在运行期的行为是动多态,通过虚函数来实现。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/zhangyumumu/article/details/46805391