封装、继承、多态
一、封装
将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,每个对象都能包含它所进行操作所需要的所有信息,而不必依赖于其他对象。数据是被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外的接口使其与外部发生联系。用户可以只关注于使用,而不关心具体的实现,用户也无需关心对象内部的细节,可以通过该对象对外提供的接口来访问该对象的内部数据。
优点:
(1)低耦合性:可以独立地进行开发、测试、优化
(2)类的内部实现可以自由的修改:可以独立的进行调试
(3)有效地调节性能:可以通过剖析来确定哪些模块影响了系统的性能
(4)可重用
(5)降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的
(6)不必关心具体实现
(7)具有安全性
(8)具有清晰的对外接口
举一个person类来说明,类封装了名字、性别、年龄等属性,外界能通过一个get方法获取一个实例person对象的name属性、性别属性,但是无法获得年龄属性,但是可以通过其它方法使用age属性。封装之后,各种属性的数据类型这些细节是用户注意不到的。
代码如下:
#include <iostream>
using namespace std;
class Person {
private:
string name;
int gender;
int age;
public:
Person(string name,int gender,int age):name(name),gender(gender),age(age){}
string getName() {
return name;
}
string getGender() {
return gender == 0 ? "man" : "woman";
}
void work() {
if (18 <= age && age <= 50) {
cout<<name << " is working very hard!";
} else {
cout<<name << " can't work any more!";
}
}
};
int main() {
Person* a=new Person("123",1,23);
a->work();
cout<<endl;
cout<<a->getName()<<endl;
cout<<a->getGender();
return 0;
}
输出:
123 is working very hard!
123
woman
二、继承
继承是实现了一种IS-A 关系,例如Cat和 Animal 就是一种 IS-A 关系,因为我们可以说cat是动物,但是不能说动物是animal,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法,构造函数不能被继承,只能被调用。继承者也可以理解为对被继承者的特殊化,因为它除了具备被继承者的特性外,还具备自己独有的个性,例如猫会上树,但不是所有的动物都能上树。很明显继承就是一种类与类之间强耦合的关系,所以继承存在缺点,父类变化,子类就不得不变,而且继承会破坏包装,父类的实现细节暴露给了子类。
子类对象能够替换掉父类对象,Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 向上转型 。例如:Animal animal = new Cat();
三、多态
多态分为静态多态和动态多态,多态是说对于同一条命令时,对于不同的对象,会做出不同的动作或者相同对象对于不同消息产生的动作。
静态多态:
是编译时多态,主要指方法的重载,早绑定,在程序编译之前就知道使用哪个函数了,即在同一个类中有相同的函数名,也就是函数重载,
例如:
A a;
a.f_1(1);
a.f_1(1,2);
动态多态:
指程序中定义的对象引用所指向的具体类型在运行期间才确定,它的特点是晚绑定,运行时多态,覆盖,重写。
例如:
#include<iostream>
using namespace std;
class base
{
public:
virtual ~base(){}
virtual void fun(){cout<<"base::fun"<<endl;}
};
class drive:public base
{
public:
virtual void fun(){cout<<"drive::fun"<<endl;}
};
int main()
{
base * p=new drive;
p->fun();
system("pause");
return 0;
}
输出是:
drive::fun
由上可以看出,运行时多态有三个条件:
继承
覆盖(重写)
向上转型
在上述的例子中,基类有一个子类,覆盖了父类的fun方法,并在main中用父类指针来指向子类对象,但是在调用fun函数时,会执行实际指向对象所在类的fun方法,而不是父类的方法。