基于日常开发、算法以及面试的需求,对C++进行粉碎性学习,会根据学习的深入重复更新。
1. OOP的基本特征
封装,继承,多态
多态性:在面向对象的编程中,多态是指程序能够通过引用或指针的动态类型获取类特定行为的能力。
2. OOP的核心特性
数据抽象、继承、动态绑定
动态绑定:函数的运行版本由实参类型决定,即在运行时选择函数版本;但是!只有通过引用或者指针调用虚函数时,才会出触发动态绑定,因此只有在这种情况下,对象的动态类型才能和静态类型不同。
3. 基类和派生类
3.1 这两个概念,通常和面向对象编程(OOP)中的继承一起出现,通过继承联系在一起的类构成一种层次关系,通常在层次关系的根部有一个基类,其它直接或间接从基类继承得到的称为派生类。
在c++11中,如果我们不希望某一个类A成为基类,可以在类名后跟一个关键字final
class A final {}
3.2 派生类的成员将隐藏同名的基类成员
4. 虚函数
在使用基类的引用或指针调用一个虚成员函数时会执行动态绑定,因此直到运行时才知道调用了哪个版本的虚函数,所以所有虚函数必须有定义!
等价概念如下:
派生类需要在内部对继承自基类的虚函数进行新的声明和定义(覆盖 overide),当使用指针或者引用调用虚函数时,将被动态绑定!
5. 虚函数 final 和 override 关键字
override是在c++11引入的新特征,用于智能判断虚函数的重新定义是否正确;final则是防止其它类覆盖。这里引用primer中的代码
struct B {
virtual void f1(int) const;
virtual void f2();
void f3();
void f5() const final; // 不允许后续的其它类覆盖
}
struct A : B {
void f1(int) override; // 正确,与基类中的f1匹配
void f2(int) override; // 错误,参数不一致
void f3() override; // 只有虚函数可以覆盖,而f3不是
void f4() override; // 没有f4()
}
6. 纯虚函数
纯虚函数无需定义,在类内部 = 0表示即可,这种表达只能出现在类内;当然我们也可以提供具体定义,必须在类的外部!
class B : public A {
public:
int pure_vir_func() const = 0;
}
7. 虚析构函数
常见的问题是:为什么析构函数是虚函数。原因在于delete某一个动态分配的对象的指针时,此时可能出现指针的静态类型和被删除对象的动态类型不符的情况,编译器需要清楚执行的是继承类的析构函数。
基类 A = new 继承类();
7. 抽象基类 : 含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类。
抽象基类负责定义接口,后续的其它类继承该类并覆盖;
抽象基类不能直接创建一个抽象基类的对象!!!!
8. struct和class的区别,唯2的区别
区别1:默认访问说明符,在struct中默认访问权限是public,而class默认是private
区别2:默认派生运算符,struct默认public继承;class默认private继承
class Base {}
struct A : Base {} // 默认public继承
class B : Base {} // 默认private继承
9. 函数调用的解析过程(名字查找优先类型查找)
假设调用a -> func( ),其中A a;首先确定a的静态类型,即A,在A类中查找名为func的函数,如果找到继续判断参数类型和返回类型;如果没有找到,则继续向基类寻找。
需要注意的是,找到对应函数并且通过类型检查(调用合法),如果func是虚函数并且是通过引用和指针调用,则编译器产生的代码将在运行时确定虚函数的版本,即动态绑定;否则将会是一个常规函数的调用。



