您的位置:首页 > 其它

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

2017-04-20 11:04 225 查看
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
b99c
() = 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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐