封装
就是把客观事物封装成抽象的类. 隐藏对象的属性和实现细节,仅对外公开接口来与对象进行交互;
继承
继承是指这样⼀种能力: 它可以使用现有类的所有功能,并在⽆需重新编写原来的类的情况下对这些功能进行扩展
继承概念的实现方式有两类:
- 实现继承:实现继承是指直接使⽤基类的属性和⽅法而无需额外编码的能⼒
- 接口继承: 接⼝继承是指仅使⽤属性和⽅法的名称、但是⼦类必需提供实现的能⼒
三种继承方式:
| 继承方式 | private继承 | protected继承 | public继承 |
|---|---|---|---|
| 基类的private成员 | 不可见 | 不可见 | 不可见 |
| 基类的protected成员 | 变为private成员 | 仍为protected成员 | 仍为protected成员 |
| 基类的public成员 | 变为private成员 | 变为protected成员 | 仍为public成员仍为public成员 |
多态
关于多态,简而言之就是用父类的指针指向其子类的实例,然后通过父类的指针调用子类的成员函数。这种技术可以让父类的指针有“多种形态”.
3.1 追问: 多态有哪些分类?
多态分为静态多态与动态多态。 分别是对应重载以及重写虚函数的方式。
静态多态:编译时期就决定了调用哪个函数, 根据参数列表来决定;
动态多态:指通过子类重写父类的虚函数来实现的,因为是在运行期间决定调用的函数, 称为动态多态。
3.2 多态与非多态的区别?
多态与⾮多态的实质区别就是函数地址绑定时机。如果函数的调⽤,在编译器编译期间就可以确定函数的调⽤地址,并产⽣代码,则是静态的,即地址早绑定。⽽如果函数调 ⽤的地址不能在编译器期间确定,需要在运⾏时才确定,这就属于晚绑定.
3.3 追问: 多态底层实现是怎么样的?
描述虚函数
3.4 追问:我希望定义一个类不能被继承,应该怎么做?
class NoInheritClass final{};class ChildClass : public NoInheritClass{};
上述代码编译结果: :::info error: cannot derive from ‘final’ base ‘NoInheritClass’ in derived type ‘ChildClass’ :::
c++11新标准提供 final 关键字能够限制类被继承
3.5 关于类型转换,以下代码是否有问题?
class Parent{};class Child : public Parent{};void TestConvert(){Child c;Parent &p = c; // 1>Parent *pp = &c; // 2>Parent ppp;Child &cc = ppp; // 3>Child *ccRef = &ppp; // 4>Child cc;Parent* ppx = &cc;Child* ccx = ppx; // 5>}
结果如下:
:::info
1> 2> 均 OK
3> 报错:error: invalid initialization of reference of type ‘Child&’ from expression of type ‘Parent’
4> 报错:error: invalid conversion from ‘Parent‘ to ‘Child‘
5> 报错:error: invalid conversion from ‘Parent‘ to ‘Child‘
:::
1> 2> 允许派生类到基类的隐式转换,因为每个派生类对象都包含一个基类部分,基类的引用或指针可以绑定到该基类部分上。
3> 4> 报错是因为一个基类对象可能是派生类对象的一部分也可能不是,所以不存在从基类向派生类的自动类型转换。
假如3> 4> 合法,那么有可能会使用 cc 或 ccRef访问 Parent中本不存在的成员。
5> 报错的原因是编译器在编译时无法确定某个特定的转换在运行时是否安全,编译器仅能通过检查指针或引用的静态类型来推断该转换是否合法。
故虽然这里使用了基类指针绑定在一个派生类对象,我们也不能执行从基类向派生类的转换。
:::info 结论:不存在从基类到派生类的隐式转换; :::
