C++——多态 上
目录一、概念二、多态的定义及实现三、动态多态的实现条件四、重写相关五、构成重写与同名隐藏的函数有什么区别六、C11 中的override 和 final一、概念通俗来说就是多种形态当完成某个行为时当不同的对象去完成时会产生不同的状态比如买票普通人买票就是全票学生买票就是半票而军人就有可能会免票。比如刷抖音小红刷到的大多都是穿搭帅哥明星小黑刷到的大多都是王者吃鸡小刚刷到的大多都是健身视频对于同一个行为不同得人最终刷到的视频内容大相径庭这就是多态。二、多态的定义及实现下面这是之前写过的动物类之间的继承Animal为基类Cat和Dog为由Animal继承下来的俩个派生类。class Animal { public: Animal(const string name, const string sex, int age) : _name(name) , _sex(sex) , _age(age) {} void sleep() { cout _name 在睡觉~~~ endl; } void eat() { cout _name 吃东西…… endl; } protected: string _name; string _sex; int _age; }; class Dog : public Animal { public: Dog(const string name, const string sex, int age, const string color) : Animal(name, sex, age) , _color(color) {} private: string _color; }; class Cat : public Animal { public: Cat(const string name, const string sex, int age, const string Tempter) : Animal(name, sex, age) , _Tempter(Tempter) {} private: string _Tempter; }; void Test1() { Dog dog(小黑, 公, 2, 金黄色); dog.eat(); dog.sleep(); Cat cat(咪咪, 母, 1, 白色); cat.eat(); cat.sleep(); } int main() { Test1(); return 0; }代码的执行中cat和dog最终的sleep与eat都是打印的同一个结果因为他们都是继承自基类都使用的是基类Animal中的同一个成员函数猫和狗二者的习性是大相径庭的所以不应该这样笼统的让它们进行同样的行为。所以给Cat类与Dog类中添加了各自的成员函数由于派生类中与基类都出现了相同名字的成员则触发被动——同名隐藏因此即会使用派生类中的成员函数。Dog void eat() { cout _name 吃的声音很大 endl; } void sleep() { cout _name 呼噜噜…… endl; } Cat void eat() { cout _name 悄咪咪的吃东西 endl; } void sleep() { cout _name zzz…… endl; }功能是实现了可是在与用户接触的Test函数中却书写了大量重复的代码使用多态第一步 使用基类引用或指针给子类赋值void Dynamic(Animal s) { s.eat(); s.sleep(); } int main() { Dog dog(小黑, 公, 2, 金黄色); Dynamic(dog); Cat cat(咪咪, 母, 1, 白色); Dynamic(cat); return 0; }第二步 将基类Animal中成员函数声明为虚函数virtual void sleep() { cout _name 在睡觉~~~ endl; } virtual void eat() { cout _name 吃东西…… endl; }完成需求实现一个绘图软件如果是圆画圆如果是矩形画矩形如果是三角形画三角形class Shape { public: virtual void Drow() { cout 图形未知无法作图 endl; } virtual double GetPerimeter() { cout 图形未知 endl; return 0; } }; class Circle : public Shape { public: Circle(double r) : _r(r) {} virtual void Drow() { cout ○ endl; } virtual double GetPerimeter() { return 2 * 3.14 * _r; } private: double _r; }; class Rectangle : public Shape { public: Rectangle(double length, double width) : _length(length) , _width(width) { } virtual void Drow() { cout □ endl; } virtual double GetPerimeter() { return 2 * (_length_width); } private: double _length; double _width; }; class Trangle : public Shape { public: Trangle(double a, double b, double c) : _a(a) , _b(b) , _c(c) {} virtual void Drow() { cout △ endl; } virtual double GetPerimeter() { return _a_b_c; } private: double _a; double _b; double _c; }; void Test(Shape s) { s.Drow(); cout s.GetPerimeter() endl; } int main() { Circle c(2.2); Rectangle r(1, 2); Trangle t(3, 4, 5); Test(c); Test(r); Test(t); return 0; }三、动态多态的实现条件1必须在继承前提下子类必须重写基类的虚函数被virtual修饰的函数即为虚函数2关于虚函数的调用通过引用或者指针3动态即只有当程序运行的时候根据传入的对象编译器才会选择类中对应的虚函数进行调用如果类中哪个方法想要实现多态的效果则该方法必须为虚函数并且在子类中必须要被重写四、重写相关1基类中要被重写的成员函数必须为虚函数2子类虚函数与基类虚函数的原型要一致返回值类型、函数名、参数列表必须完全一致特例① 析构函数一般定义为虚函数基类与子类函数名不同但构成重写② 协变基类函数返回基类的指针或引用子类返回子类的指针或引用返回值类型不同但也构成重写3子类中的virtual关键字有没有均可推荐加上4基类与子类的虚函数访问权限可以不同一般保持一致class B { public: virtual ~B() //特例 ① virtual修饰基类的析构函数 { cout B::~B() endl; } virtual B* GetObjPtr() //特例 ② 基类返回基类的指针子类返回子类的指针 { return this; } }; class D : public B { public: ~D() { cout D::~D() endl; } virtual D* GetObjPtr() { return this; } }; void Test(B* t) { delete t; t nullptr; } void TestAddr(B* t) { if (t) { t-GetObjPtr(); } } int main() { B *pb new B; Test(pb); D* pd new D; Test(pd); /////////////////////////////////// B b; TestAddr(b); D d; TestAddr(d); return 0; }五、构成重写与同名隐藏的函数有什么区别1相同点1、都在同一个继承体系下一个在基类中一个在子类中2、二者的函数名相同2不同点1、重写中基类的函数必须是虚函数而同名隐藏没有要求2、重写要求俩个函数的原型必须一致析构函数与协变除外而同名隐藏只要求俩个函数函数名相同下列代码只有func1构成重写class B { public: virtual void func1() { cout B::func1 endl; } void func2() { cout B::func2 endl; } void func3() { cout B::func3 endl; } void func4(int a) { cout B::func3 endl; } }; class D : public B { public: virtual void func1() { cout D::func1 endl; } void func2() { cout D::func2 endl; } virtual void func3() // 子类中为虚函数 基类中不为虚函数 (不构成重写) { cout D::func3 endl; } virtual void func4(int a) // 子类中为虚函数 基类中不为虚函数 (不构成重写) { cout D::func4 endl; } };六、C11 中的override 和 final从上面可以看出C对函数重写的要求比较严格但是有些情况下由于疏忽可能会导致函数名字母次序写反而无法构成重载而这种错误在编译期间是不会报出的只有在程序运行时没有得到预期结果才来debug会得不偿失因此C11提供了override和final两个关键字可以帮助用户检测是否重写final修饰类表示该类不能被继承。1final修饰虚函数表示该虚函数不能再被重写最后一个了嘛2override只能修饰子类中的虚函数指定该虚函数必须要实现重写基类中函数如果无法实现则报错