当前位置: 首页 > news >正文

网站建站报价单网站建设中 模版下载

网站建站报价单,网站建设中 模版下载,手机网站制作电话,威海网站制作多态的概念 通俗来说#xff0c;就是多种形态#xff0c;具体点就是去完成某个行为#xff0c;当不同的对象去完成时会产生出不同的状态。 举个简单的例子#xff1a;抢红包#xff0c;我们每个人都只需要点击一下红包#xff0c;就会抢到金额。有些人能… 多态的概念         通俗来说就是多种形态具体点就是去完成某个行为当不同的对象去完成时会产生出不同的状态。         举个简单的例子抢红包我们每个人都只需要点击一下红包就会抢到金额。有些人能抢到几十元而有些人只能抢到几元甚至几毛。也正说明了不同的人做相同的事结果却不同这就是多态。 在C中有两种多态性一种是静态的多态、一种是动态的多态 静态的多态函数重载,看起来调用同一个函数却有不同的行为。静态原理是编译时实现。 动态的多态一个父类的引用或指针去调用同一个函数传递不同的对象会调用不同的函数。动态原理是运行时实现。 一、前言 多态按字面的意思就是多种形态。当类之间存在层次结构并且类之间是通过继承关联时就会用到多态。C 多态意味着调用成员函数时会根据调用函数的对象的类型来执行不同的函数。下面的实例中基类 Shape 被派生为两个类如下所示 #include iostream using namespace std;class Shape { public:void area(){cout Parent class area : endl;} }; class Rectangle : public Shape { public:void area(){cout Rectangle class area : endl;} }; class Triangle : public Shape { public:void area(){cout Triangle class area : endl;} };void func(Shape p) {p.area(); } // 程序的主函数 int main() {Rectangle Rec;// 调用矩形的求面积函数 areafunc(Rec);Triangle Tri;// 调用三角形的求面积函数 areafunc(Tri);return 0; } 当上面的代码被编译和执行时它会产生下列结果 Parent class area : Parent class area :导致错误输出的原因是调用函数 area() 被编译器设置为基类中的版本这就是所谓的静态多态或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定因为 area() 函数在程序编译期间就已经设置好了。 但现在让我们对程序稍作修改在 Shape 类中area() 的声明前放置关键字 virtual其余不变如下所示 #include iostream using namespace std;class Shape { public:virtual void area(){cout Parent class area : endl;} }; class Rectangle : public Shape { public:void area(){cout Rectangle class area : endl;} }; class Triangle : public Shape { public:void area(){cout Triangle class area : endl;} };void func(Shape p) {p.area(); } // 程序的主函数 int main() {Rectangle Rec;// 调用矩形的求面积函数 areafunc(Rec);Triangle Tri;// 调用三角形的求面积函数 areafunc(Tri);return 0; } 修改后当编译和执行前面的实例代码时它会产生以下结果 Rectangle class area : Triangle class area :此时编译器看的是指针的内容而不是它的类型。因此由于 tri 和 rec 类的对象的地址存储在 *shape 中所以会调用各自的 area() 函数。 正如您所看到的每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态您可以有多个不同的类都带有同一个名称但具有不同实现的函数函数的参数甚至可以是相同的。 二、多态的定义及实现 1.多态的构成条件  在继承中要构成多态还有两个条件 1必须通过基类的指针或者引用调用虚函数。 2被调用的函数必须是虚函数且派生类必须对基类的虚函数进行重写。 2.虚函数 虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时会告诉编译器不要静态链接到该函数。         我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数这种操作被称为动态链接或后期绑定。 一旦定义了虚函数该基类的派生类中同名函数也自动成为了虚函数。也就是说在派生类中有一个和基类同名的函数只要基类加了virtual修饰派生类不加virtual修饰也是虚函数。虚函数只能是类中的一个成员函数不能是静态成员或普通函数。 注意我们在继承中为了解决数据冗余和二义性的问题需要用到虚拟继承关键字也是virtual和多态中的virtual是没有关系的。 3.虚函数的重写 虚函数的重写(覆盖)派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同)称子类的虚函数重写了基类的虚函数。 通过对虚函数的重写就能够实现多态 #includeiostream using namespace std;//买票 class Person { public:virtual void BuyTicket() { cout 买票-全价 endl; } };//学生买票 class Student : public Person { public:virtual void BuyTicket() { cout 买票-半价 endl; } };//军人买票 class Soldier : public Person { public:void BuyTicket() { cout 优先-买票-半价 endl; }};//构成多态传的哪个类型的对象调用的就是这个类型的虚函数 --- 跟对象有关 //不构成多态调用就是P的类型 --- 跟类型有关 void Func(Person p) //或void Func(Person* p) {p.BuyTicket(); //p-BuyTicket(); }int main() {Person ps;Func(ps); //没有任何身份去买票一定是全价Student st;Func(st); //以学生的身份去买票是半价Soldier so;Func(so); //以军人的身份去买票是优先并且半价return 0; } 4.虚函数重写的两个例外 (1).协变(基类与派生类虚函数返回值类型不同) 派生类重写基类虚函数时与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用派生类虚函数返回派生类对象的指针或者引用时称为协变。  另一种解释 C中的协变Covariance指的是派生类的返回类型可以是基类函数的返回类型的子类型。当一个派生类继承了一个基类并且覆盖override了基类中的虚函数时可以使用协变来改变返回类型。 具体而言如果基类函数的返回类型是指针或引用那么派生类中覆盖该函数时返回类型可以是基类返回类型所指向或引用的类型的派生类型。 实现协变需满足以下条件 基类中的函数必须是虚函数使用 virtual 关键字声明。派生类中重写的函数必须具有相同的函数签名函数名、参数列表和常量性。派生类中重写的函数的返回类型必须是基类函数返回类型的子类型。 示例 引用自C协变(covariant)-CSDN博客 假设有一个基类 Animal 和两个派生类 Dog 和 Cat。Animal 类中有一个虚函数 makeSound()它返回一个指向 Animal 对象的指针。在派生类 Dog 中可以重写 makeSound() 函数并返回一个指向 Dog 对象的指针。同样在派生类 Cat 中也可以重写 makeSound() 函数并返回一个指向 Cat 对象的指针。 #include iostream class Animal { public:virtual Animal* makeSound() {std::cout Animal makes a sound. std::endl;return this;} }; class Dog : public Animal { public:virtual Dog* makeSound() {std::cout Dog barks. std::endl;return this;} }; class Cat : public Animal { public:virtual Cat* makeSound() {std::cout Cat meows. std::endl;return this;} }; int main() {Animal* animal;Dog dog;Cat cat;animal dog;animal-makeSound(); // Output: Dog barks.animal cat;animal-makeSound(); // Output: Cat meows.return 0; }协变与多态的区别  C中协变和多态是密切相关的。多态Polymorphism指的是同一个函数在不同的对象上被调用时可以表现出不同的行为方式。 在C中通过使用虚函数virtual function实现了运行时多态的语法机制。基类中的虚函数可以在派生类中被重写覆盖当派生类对象调用该虚函数时会根据对象的实际类型来确定调用哪个虚函数。 而协变则指的是派生类可以改变继承自基类函数的返回类型使得返回类型成为基类返回类型所指向或引用的类型的派生类型。 通过将协变和多态结合起来我们可以在派生类中覆盖基类的虚函数并且返回派生类特有的类型。这就允许我们在多态的情况下在派生类中使用更具体的返回类型。 (2).析构函数的重写基类与派生类析构函数的名字不同  如果基类的析构函数为虚函数此时派生类析构函数只要定义无论是否加virtual关键字都与基类的析构函数构成重写虽然基类与派生类析构函数名字不同。虽然函数名不相同看起来违背了重写的规则其实不然这里可以理解为编译器对析构函数的名称做了特殊处理编译后析构函数的名称统一处理成destructor。 在C中析构函数是一种特殊的成员函数用于在对象销毁时执行清理工作。通常情况下析构函数会自动由编译器生成默认执行对象的成员变量和基类的析构函数。 当需要对派生类进行额外的清理工作或资源释放时可以通过重写override基类的析构函数来实现。 在派生类中重写析构函数需要遵循以下规则 函数名与基类的析构函数完全相同。参数列表为空。返回类型为空void。可以添加override关键字可选以显式地说明正在重写基类的析构函数。 以下是一个示例代码 class Base { public:virtual ~Base() {// 基类的析构函数} };class Derived : public Base { public:~Derived() override {// 派生类的析构函数重写了基类的析构函数} };在上述代码中基类Base定义了一个虚析构函数派生类Derived通过重写基类的析构函数实现了自己的清理逻辑。 需要注意的是在继承关系中如果基类的析构函数是一个虚函数则派生类中的析构函数也应该声明为虚函数。这样在使用基类指针或引用指向派生类对象并通过该指针或引用调用析构函数时能够正确地调用到派生类的析构函数。 总之通过在派生类中重写基类的析构函数可以实现额外的清理工作或资源释放。重写析构函数需要遵循特定的规则并且建议将基类的析构函数声明为虚函数。 另一种解释 (引用自C 多态一 多态的构成条件、final、override、协变、析构函数的重写、抽象类_c 多态 override-CSDN博客) 析构函数虽然函数名不同但是也能构成重写因为站在编译器的视角他所调用的析构函数名称都叫做destructor。 为什么编译器要通过这种方式让析构函数也能构成重写呢 假设我用一个基类指针或者引用指向派生类对象如果不构成多态会怎样 class Human { public:~Human(){cout ~Human() endl;} };class Student : public Human { public:~Student(){cout ~Student() endl;} };int main() {Human* h new Student;delete h;return 0; }输出结果  ~Human() 分析 上述代码只会调用类Human的析构函数即如果不构成多态那么指针是什么类型的就会调用什么类型的析构函数这也就导致了一种情况如果派生类的析构函数中有资源释放而这里却没有释放掉那些资源就会导致内存泄漏的问题。 所以为了防止这种情况必须要将析构函数定义为虚函数。这也就是编译器将析构函数重命名为destructor的原因。 class Human { public:virtual ~Human(){cout ~Human() endl;} };class Student : public Human { public:virtual ~Student() // 该virtual关键字可省略{cout ~Student() endl;} };int main() {Human* h new Student;delete h;return 0; }输出结果  ~Student() ~Human()  5.C11 override和final 从上面可以看出C对函数重写的要求比较严格但是有些情况下由于疏忽可能会导致函数名字母次序写反而无法构成重载而这种错误在编译期间是不会报出的只有在程序运行时没有得到预期结果才来debug会得不偿失因此C11提供了 override 和 final 两个关键字可以帮助用户检测是否重写。 (1) final 在C11标准中final是一个关键字用于禁止继承和覆盖类的虚函数。当一个类或者一个类的成员函数被声明为final时意味着它不能再被其他类继承或者它的虚函数不能被派生类覆盖。 使用final关键字的好处是 可以增强代码的安全性使用final关键字可以防止不恰当的继承和覆盖。可读性使用final关键字可以增强代码的可读性和可维护性明确了类或函数的意图。 final修饰虚函数表示该虚函数不能再被重写。 #includeiostream class Car { public:virtual void Drive() final{} };class Benz :public Car { public:virtual void Drive() override //检查是否完成重写{std::cout Benz-舒适 std::endl;} }; int main() {Benz benz;benz.Drive(); } 上述程序因为final关键字的存在会报错报错原因是 final修饰类表示该类不能再被继承。 示例 #includeiostream class Car final { public:virtual void Drive() {} };class Benz :public Car { public:virtual void Drive() //检查是否完成重写{std::cout Benz-舒适 std::endl;} }; int main() {Benz benz;benz.Drive(); } 上述程序报错  不能将final用于基类否则程序报错  (2) override 在C中override是一个特殊的关键字用于显式地标识派生类中的函数是覆盖override基类中的虚函数。 当派生类中的函数与基类中的虚函数具有相同的名称、参数列表和返回类型时可以使用override关键字来明确指示该函数是对基类函数的覆盖。 使用override关键字的好处是 错误检查编译器会在编译时检查是否存在函数覆盖错误。如果派生类中使用了override关键字但没有正确地覆盖基类中的虚函数编译器将报错。可读性使用override关键字可以增强代码的可读性和可维护性明确了派生类函数的意图。 下面是一个示例代码 #includeiostream class Car { public:virtual void Drive(){} };class Benz :public Car { public:virtual void Drive() override //检查是否完成重写{std::cout Benz-舒适 std::endl;} }; int main() {Benz benz;benz.Drive(); // Benz-舒适 } 三、抽象类 纯虚函数 您可能想要在基类中定义虚函数以便在派生类中重新定义该函数更好地适用于对象但是您在基类中又不能对虚函数给出有意义的实现这个时候就会用到纯虚函数。 在虚函数的后面写上  0 则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类也叫接口类抽象类不能实例化出对象。 class Shape {public:// pure virtual functionvirtual int area() 0; }; 0 告诉编译器函数没有主体上面的虚函数是纯虚函数。 包括纯虚函数的类叫做抽象类也叫接口类抽象类不能实例化出对象。 示例 #includeiostream //抽象类 class Car { public:virtual void Drive() 0;//纯虚函数 };int main() {Car c;//抽象类不能实例化出对象return 0; } 上述程序运行报错  派生类继承后也不能实例化出对象。 示例 #includeiostream class Car { public:virtual void Drive() 0; // 纯虚函数 }; class Benz :public Car{}; int main() {Benz b1; } 上述程序运行出错  派生类继承后也不能实例化出对象只有重写纯虚函数派生类才能实例化出对象。纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承。 示例 #includeiostream class Car { public://纯虚函数一般只声明不实现可以实现但没有价值因为不能实例化出对象可以定义指针或引用virtual void Drive() 0; };class Benz :public Car { public:virtual void Drive(){std::cout Benz-舒适 std::endl;} };class BMW :public Car { public:virtual void Drive(){std::cout BMW-操控 std::endl;} };int main() {//派生类只有重写了纯虚函数才能实例化出对象Benz b1;BMW b2;//通过基类的指针去调用不同对象的函数Car* pBenz new Benz;pBenz-Drive();Car* pBMW new BMW;pBMW-Drive(); } 输出结果  Benz-舒适 BMW-操控 接口继承和实现继承 普通函数的继承是一种实现继承派生类继承了基类的普通成员函数可以使用函数继承的是函数的实现。虚函数的继承是一种接口继承派生类继承的是基类虚函数的接口目的是为了重写达成多态继承的是接口。所以如果不实现多态不要把函数定义成虚函数。 参考自很值得学习 【精选】【C】—— 多态_c多态_霄沫凡的博客-CSDN博客 注参考内容只是为了自身学习并无其他想法
http://www.zqtcl.cn/news/790230/

相关文章:

  • 怎么做hs网站最专业的网站开发公司哪家最专业
  • 南京做网站的公司排名科技:开局研发六代战机许禾
  • 网站怎么做搜索引擎淘宝网站怎么做特价
  • 仿制网站建设oa办公系统官网
  • 深圳网站托管企业建站源码系统
  • 个人空间网站建设报告建站是什么东西
  • 好看的模板网站建设西安网站模板建站
  • 建设网站二级子页打不开广告设计平面设计培训班
  • 网站公司做网站要多少钱新乡
  • 天津谁做网站莱芜人才网招聘网
  • 学做网站的书籍自己做网站 最好的软件
  • 手机网站专题电商入门视频教程免费
  • aspx网站模板制作网页常用的软件有哪些
  • 网站主关键词湖南网站定制
  • 长沙seo网站排名优化公司进入秦皇岛最新规定
  • 企业网站优化平台宝山北京网站建设
  • 给人做代工的网站加盟代理网
  • 网站建设用dw电脑谷歌浏览器打开是2345网址导航
  • 做外贸一般总浏览的网站太原的网站建设公司哪家好
  • 台州建网站公司wordpress 用微信登陆
  • 广州白云网站建设家在深圳业主
  • 呼和浩特网站建设哪家最便宜?携程旅行网网站策划书
  • 网站建设及相关流程北京网站备案域名
  • 汉字叔叔花了多少钱做网站微商城科技
  • 网站代理被抓html网站开发实战
  • 如何建立免费的网站网站copyright写法
  • 官方网站下载12306合肥有没有做网站的单位
  • 甘露园网站建设网站框架图片
  • 做网站怎样赚卖流量石家庄网站建设联系电话
  • wordpress 图片网站本地免费发布信息网站