封装
封装即通过类将对象的属性以及方法结合起来,仅对外提供接口。
封装的好处
- 提高安全性,提高代码的复用性。
- 使得整个工程更加容易
- 可以避免命名冲突的问题
继承
继承:不同继承方式下派生类的继承模型,分为共有继承和私有继承两种。
- 单继承:一个类只有一个基类
- 多继承:一个类具有多个基类,继承列表中,每个基类前必须加继承权限,否则会采用默认继承。
公有继承
理解
共有继承体现”是一个“含义。公有继承中对基类所适用的任何东西,也都适用于派生类。
如:虽然正方形是特殊的矩形,但是对象改变时,长方形长宽不需要同时变而正方形需要同时变,所以不能说正方形共有继承于矩形。
当类D从类B公有继承时,每一个D是一个B,反之不成立。如:
class Person
{
public:
Person(int age = 0)
:_age(age)
, _addr()
, _name()
{}
private:
char _name[20];
char _addr[30];
int _age;
};
class Student :public Person
{
;
};
可以说每个学生是一个人,但不可以说每个人都是学生。
区分接口继承和实现继承
- 纯虚函数:必须在继承的类中重新声明,并且在抽象类中常无定义。【纯虚函数使基类成为抽象类,不可实例化】
- 非纯虚函数:子类必须支持这个函数的实现,若子类中不提供实现方法则采用基类中的。
- 非虚函数:不会改变行为,不可以在子类中重新定义。为这个类建立一种特殊的不变性。
私有继承
理解
私有继承体现“用…实现”,如果D私有继承自B,D的对象用B的对象实现,不存在概念上的关系。
- 与公有继承相反,编译器不会将派生类对象转化为基类对象。
- 私有继承而来的成员都是派生类中的私有成员。(无论它们在基类中是什么成员)
分层和私有继承
分层:体现“有一个”或“用…实现”,通过将某个类的对象成为另一个类的数据成员来实现。【平时在体现“用…实现使”优先使用】
私有继承:只有继承才能访问私有成员,只有私有继承可以使虚函数重新定义。【有虚函数需要重新定义时优先使用】
继承和模板
对于一个类而言,若对于不同的类型,其方式完全不同,则更加适合用继承。【继承不适合创建堆栈】
当对象类型不影响类中函数的行为时,使用模板生成这一组类。
多态
理解
多态即某一个事物在不同的情况体现不同的状态,即为多态。可分为静态类型的多态和动态类型的多态。
C++中实现多态
静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。
C++中共有三种实现多态的方式。。第一种是函数重载;第二种是模板函数;第三种是虚函数(函数覆盖)。
前提
- 有继承的关系
- 基类中必须有虚函数,派生类必须对基类的虚函数进行重写
- 虚函数的调用:通过基类的指针或引用调用虚函数
若多态实现条件没有满足,全部调用基类的虚函数。
重写
若用final关键字修饰虚函数,则虚函数不能被派生类重写。
- 派生类重写基类里的虚函数
- 派生类虚函数必须与基类虚函数原型完全相同(返回值类型、函数名、参数列表)
- 派生类中函数前最好加上virtual
什么时候把基类虚构函数设为虚函数?
若派生类中涉及资源管理,那么最好将基类中析构函数设为虚函数。此时一旦没有构成重写,编译器不会报错但不会实现多态。
例外
- 协变:基类对象返回基类指针(引用),子类对象返回子类指针(引用)
- 析构函数:只要基类中的析构函数为虚函数,子类中的析构函数一旦提供就与基类中析构函数构成重写。
体现
在代码运行时,根据基类的指针(引用)指向不同子类的对象,调用对应的子类的虚函数。
如:
class Person
{
public:
Person(int age = 0)
:_age(age)
, _addr()
, _name()
{}
virtual void show()
{
cout << "父类方法"<<endl;
}
private:
char _name[20];
char _addr[30];
int _age;
};
class Student :public Person
{
public:
Student()
:_name()
, _addr()
, _age()
{}
virtual void show()
{
cout << "重写父类方法"<<endl;
}
private:
char _name[20];
char _addr[30];
int _age;
};
int main()
{
Student s;//体现学生的状态
Person p = s;//体现人的状态
p.show();
s.show();
return 0;
}
输出: