欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类)

发布时间:2025/3/21 45 豆豆
生活随笔 收集整理的这篇文章主要介绍了 第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.
1、重载/覆盖 PK 重写/重定义 【预备知识】 函数重载必须在同一个类中发生子类无法重载父类的函数,父类同名的函数将会被名称覆盖重载是在编译期间根据参数类型和个数决定函数调用重载只放在同一个类之中,在编译期间就确定 函数重写必须发生在父类与子类之间父类与子类中的函数必须有完全相同的函数原型使用virtual关键字声明后能够产生多态(如果没有virtual,那叫重定义)多态是运行期间根据具体对象的类型决定函数调用重写发生在父子类之间, 覆盖父类和子类中有“同名”的函数,子类的函数会把父类的函数隐藏起来 重定义(是一种特殊的覆盖)父类和子类中有“相同函数原型”的函数,子类的函数会把父类的函数隐藏起来 1、重载/覆盖 PK 重写/重定义重载:在“同一个类”中,函数名相同、函数原型不同,此时发生重载覆盖:无virtual关键字,在父类、子类中,函数名相同、函数原型不同,此时在子类中的函数隐藏了父类中的函数。 ---------------------------------------------------------------------------------------------重写:有virtual关键字,在父类、子类中,函数名、函数原型都相同,此时发生重写重定义:无virtual关键字,在父类、子类中,函数名、函数原型都相同,此时在子类中的函数隐藏了父类中的函数,类似于“覆盖”。#include <iostream> using namespace std; class B { public:void f() { }virtual void f(int i) { } void f(int i,int j) { } }; class D:public B { public://子类中没有void f()函数void f(int i) { } //重写void f(int i,int j) { } //发生名称覆盖void f(int i,int j,int k) { } //发生名称覆盖 }; void g(B& b) {b.f(1); }void main() {D d; /***************************************************************************/ //【重点】 //疑问:为什么子类对象不能调用父类中的f()函数? //答:因为子类中有函数名为f的函数,有由于子类的函数不会重载父类的函数,所以 //子类中的f函数会把父类中的无参的f()函数给覆盖,因此直接d.f()会发生编译错误!//d.f();//error: 没有重载函数接受0个参数的f() //疑问:如果我就是想用子类对象去调用父类中的f()函数,应该怎么做? //答:加上作用域符B::,此时d对象就会调用父类B中的f()函数。d.B::f(); // //【结论】子类中的f()函数不会重载父类中的f()函数,父类同名的函数将被覆盖!重载只发生在同一个类中! /***************************************************************************/D dd; //覆盖//dd.f(); //编译失败,因为无virtual发生同名覆盖dd.B::f(); //作用域B::,调用子类中的void f() //覆盖、重定义dd.B::f(1,2);//作用域B::,调用子类中的void f(int i,int j)dd.f(1,2); //覆盖:调用子类中的void f(int i,int j) //多态B &b = d;b.f(1); //多态:调用子类中的void f(int i)b.B::f(1); //作用域B::,调用父类中的void f(int i) //重载 } ---------------------------------------------------------------------- 2、父类对象、子类对象混搭风【该模块中,隐藏了一个天大的Bug:“P++步长”】本质:由于步长的影响 【结论】不要用父类指针p指向子类对象的数组,通过p++,去遍历这个子类对象的数组。 同理:也不要用子类指针p指向父类对象的数组,通过p++,去遍历这个父类对象的数组 【为什么?】因为父类指针p指向了子类对象的数组,在进行p++的时候, p增加的步长是sizeof(父类),但是由于sizeof(父类)不一定等于sizeof(子类), 因此p在进行加1后,指向的位置不一定是下一个子类对象的首地址。当p指向的位置 不是下一个子类对象的首地址的时候,如果进行访问子类对象中的成员,程序必然会发生 崩溃。 //程序案例 #include <iostream> using namespace std; class A { public:virtual void f(){cout<<"A:f()"<<endl;} }; class B:public A { public:int i; //为什么在子类中加上一个属性i,程序就会运行崩溃:因为加了一个变量,对步长有影响 public:B(int i=0,int j=0){}virtual void f(){cout<<"B:f()"<<endl;} };void howToF(A* pBase) {pBase->f(); }int main() {A *p = NULL;B *q = NULL;B c[3] = {B(1,1),B(1,1),B(1,1)};p = c; //父类指针p指向由子类对象构成的数组q = c; //子类指针q指向由子类对象构成的数组for(int i=0;i<3;i++){//p->f(); //因为步长的原因,访问成员时,会发生程序崩溃q->f();p++; //p++增加的步长是sizeof(父类),而不是sizeof(子类)q++;}for(int i=0;i<3;i++){howToF(&c[i]); //形参:子类对象的地址,实参:父类指针//解释:此处运行正确,为什么? //此处没有用p++形式,而是用了下标操作c[i],避开了p++的步长不一致导致的程序崩溃。}return 0; } ---------------------------------------------------------------------- 3、抽象类 #include <iostream> using namespace std; class A { public:virtual void ff() = 0; };//A g1(); //不允许使用返回抽象类 "A" 的函数 A& g21(); //允许使用返回抽象类引用类型 "A&" 的函数 A& g22(A&); A& g23(A*); A* g31(); //允许使用返回抽象类指针类型 "A*" 的函数 A* g32(A*); A* g32(A&);//void f1(A a);//不允许使用抽象类类型 "A" 的参数 void f2(A &a);//允许使用抽象类引用类型 "A&" 的参数 void f2(A *a);//允许使用抽象类指针类型 "A*" 的参数class B:public A { public:void ff() { } }; int main() {//A a1;//不能实例化抽象类//A a2 = new A;//不能实例化抽象类A *a4 = new B; //可以用抽象类的指针指向抽象类的子类对象B b1;A &a3 = b1;//可以用抽象类的引用指向抽象类的子类对象 }4、多重继承与抽象类--->实现多继承接口 【知识复习】 #include <iostream> using namespace std; class A1 { public:virtual void ff() = 0; //纯虚函数void gg() { cout<<"普通函数"<<endl; } //普通函数在A1中实现 }; class A2 { public:virtual void ff() = 0; //纯虚函数void gg() { cout<<"普通函数"<<endl; }//普通函数在A2中实现 }; class B:public A1,public A2//B多重继承A1、A2时 { public:virtual void ff() //纯虚函数ff在B中实现{ cout<<"纯虚函数"<<endl; }}; int main() {B b; //b访问纯虚函数ff,不会发生二义性b.ff(); //b访问普通函数gg,会发生二义性//b.gg(); //编译失败 //防止二义性,应该加上作用域标识符A1::、A2::b.A1::gg();b.A2::gg(); } 【知识引出】 疑问:多重继承会发生二义性,但是多重继承在C++中有什么作用呢? 答:在项目开发过程中,多重继承的作用主要是:多重继承多个抽象类(接口),这样会有效的避免二义性。多继承接口案例 #include <iostream> using namespace std; class A { public:virtual void ff(){ cout<<"ff:A"<<endl; } };class interface1 //抽象类接口1 { public:virtual void gg1() = 0; }; class interface2 //抽象类接口2 { public:virtual void gg2() = 0; }; class B:public A,public interface1,public interface2 { //B类继承了类A、接口interface1、接口interface2 public:void gg1() { cout<<"gg1"<<endl; }void gg2() { cout<<"gg2"<<endl; }void kk(){ cout<<"KK"<<endl; } }; int main() {B b;b.ff();b.gg1();b.gg2();b.kk(); }

总结

以上是生活随笔为你收集整理的第九天2017/04/18(3、重载/覆盖 PK 重写/重定义、父类子类混搭风、抽象类)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。