多态总结

1. C++ 多态分类及实现 重载多态(Ad-hoc Polymorphism,编译期):函数重载、运算符重载(静态多态、静态编译) 子类多态(Subtype Polymorphism,运行期):虚函数(动态多态、动态编译) 参数多态(Parametric Polymorphism,编译期):类模板(泛型)、函数模板(函数指针) 强制多态(Coercion Polymorphism,编译期/运行期):基本类型转换、自定义类型转换 2. 虚表指针、虚函数指针、虚函数表 虚表指针:在含有虚函数的类的对象中,指向虚函数表的指针,在运行时确定 虚函数指针:指向虚函数的地址的指针 {vfptr} 虚函数表:在程序只读数据段,存放虚函数指针,如果派生类实现了基类的某个虚函数,则在虚函数表中覆盖原本基类的那个虚函数指针,在编译时根据类的声明创建 Shape::$vftable@ class Shape { public: virtual ~Shape(){ cout << "~Shape()" << endl; } }; class Point { public: ~Point(){ cout << "~Point()" << endl; } private: int m_x{0}; int m_y{0}; char m_c; }; class Circle : public Shape { public: ~Circle(){ cout << "~Circle()" << endl; } private: Point m_p; }; int main( ) { // 8 类中存有指向虚函数表的指针 cout << sizeof(Shape) << endl; // 16 类中存有指向虚函数表的指针 // 以及Point对象的大小(此时Point类中不包含任何变量),虽然真实大小是 8 + 1 // 但是额外多的1个字节导致内存大小扩增了 8(每次扩增的最小数值是8 <alignment member> (size=7)) cout << sizeof(Circle) << endl; // 8 类中的int占4个字节,char占1个字节 // 但每次扩增的最小数值是4(<alignment member> (size=3)) cout << sizeof(Point) << endl; Circle c; return 0; } Start 8 24 12 ~Circle() ~Point() ~Shape() 0 Finish Shape 类...

December 27, 2021 · 2 min · Rick Cui

函数默认参数和占位参数

1. 函数默认参数和占位参数(亚元) 形参中的占位参数又叫亚元,并没有什么实际意义,只在函数重载中起到作用 // 亚元,设置了默认值所以调用时可以不传参数 // 否则就必须传入两个参数 void foo(int a, int = 0){ cout << "a = " << a << endl; } int main() { foo(20); return 0; } 2. 函数重载 函数名相同,形参列表不同(形参个数、类型、顺序) 函数返回值不起作用 函数重载和默认参数不要同时使用(函数调用时容易产生二义性) 倾轧技术(name mangling),底层会将函数名进行编译 用 v c i f l d 表示 void char int float long double 及其引用 int fun(int) => fun_i int fun(int, char, double) => fun_icd 重载函数匹配顺序 如果能够严格匹配,则调用完全匹配的 如果没有完全匹配的,则调用隐式转换的 都匹配不上,编译失败 3....

December 26, 2021 · 1 min · Rick Cui

内存区间划分

一、程序运行前 代码区(只读的、共享) 数据区(全局变量和静态变量) 二、程序运行后 栈区 堆区 全局静态区(全局变量、静态变量、常量) 代码区 Linux程序运行内存虚拟地址空间: 三、全局变量与静态变量的区别 作用域不同 全局变量默认是 extern 的 静态变量是文件作用域 C/C++ 中 extern 关键字详解

December 26, 2021 · 1 min · Rick Cui

指针引用

指针也是一种变量,作为函数形参和返回值的时候也是值拷贝(拷贝的是一个地址) 使用指针引用的方式,代码更加简洁,逻辑更加清晰 释放掉指针指向的空间后,一定记得把指针置空 声明指针时就进行初始化或将其指向 NULL 是个好习惯 指针值拷贝: void test(int * a){ a = new int(20); //*a = 20; cout << "test point a = " << &a << endl; cout << "test:" << a << "\ta = " << *a << endl; } int main() { int *a = new int(10); cout << "main point a = " << &a << endl; cout << "main:" << a << "\ta = " << *a << endl; test(a); cout << "main:" << a << "\ta = " << *a << endl; return 0; } 输出:...

December 25, 2021 · 2 min · Rick Cui

抽象、封装、继承、多态

一、抽象、封装 数据抽象:是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制,是一种依赖于接口实现分离的设计技术 数据封装:是一种把数据和操作数据的函数捆绑在一起的机制 1. 好处 类的内部受到保护,不会因无意的用户级错误导致对象状态受损 类实现可能随着时间的推移而发生变化,数据抽象可以更好的应对不断变化的需求 2. 策略 通常情况下,我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性。 抽象把代码分离为接口和实现。所以在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。在这种情况下,不管任何程序使用接口,接口都不会受到影响,只需要将最新的实现重新编译即可 3. 接口 接口描述了类的行为和功能,而不需要完成类的特定实现。如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类 设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。因此,如果一个 ABC 的子类需要被实例化,则必须实现每个虚函数,如果没有在派生类中重写纯虚函数,就尝试实例化该类的对象,会导致编译错误。 可用于实例化对象的类被称为具体类 接口的好处实现了解耦合的作用。 可以将软件架构分为业务逻辑层、抽象层和实现层 二、继承 继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。类派生列表以一个或多个基类命名 派生类可以访问基类中所有的非私有成员,同时,一个派生类继承了所有的基类方法,但下列情况除外: 基类的构造函数、析构函数和拷贝构造函数 基类的重载运算符 基类的友元函数 三、多态 虚函数:虚函数是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链编到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链编,或后期绑定。 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数 一般要将父类的析构函数设置为虚函数,如果不把父类的析构函数设置为虚函数,在 delete 父类指针时就不会调用子类的析构了 类析构顺序:1)派生类本身的析构函数;2)对象成员析构函数;3)基类析构函数 若在基类中不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数,在函数参数后直接加 = 0 告诉编译器,函数没有主体,这种虚函数即是纯虚函数 测试类: // 基类 Shape class Shape { protected: int width, height; public: Shape(int a = 0, int b = 0) { width = a; height = b; } virtual ~Shape() { cout << "Shape destructor" << endl; } // pure virtual function virtual int area() = 0; }; // 基类 PaintCost class PaintCost { public: int getCost(int area) { auto res = area * 70; cout << "PaintCost: " << res << endl; return res; } }; class Rectangle: public Shape, public PaintCost { public: Rectangle(int a = 0, int b = 0):Shape(a, b) { } ~Rectangle() { cout << "Rectangle destructor" << endl; } void printPro() { // 访问父类的成员变量(不能访问父类的私有成员) cout << "width: " << width << "\theight: " << height << endl; } int area () { auto area = width * height; cout << "Rectangle class area: " << area <<endl; return area; } }; class Triangle: public Shape { public: Triangle(int a = 0, int b = 0):Shape(a, b) { } ~Triangle() { cout << "Triangle destructor" << endl; } int area () { auto area = width * height / 2; cout << "Triangle class area: " << area <<endl; return area; } }; 多继承:...

December 24, 2021 · 2 min · Rick Cui