继承和派生

继承

对于privateprotectedpublic三种继承方式,除了基类的私有成员在派生类中不可直接访问外,其他访问权限在派生类中均可直接访问

class A {
    int a;
public:
    A(int n):a(n){};
    int get(){ return a;}
};

class B:protected A
{
    int b;
public:
    B(int m,int n): A(n),b(m){}
    void getb(){
        cout<<b<<endl;
        cout<<get();

    }
};
//在类B中,还隐含了
private: int a;
protected: int get(){ return a;}

私有继承

基类的所有成员在派生类中均变成private,而这些private又可以分为在基类中原本是private和原本是
proteceted以及public
前者在派生类中不能直接访问,一般是通过继承下来可以访问基类私有成员的公有成员函数,进而获得基类的私有成员;而后者,在派生类中是可以直接访问;

保护继承

私有继承类似,在派生类中,继承下来的基类的private成员不能直接访问,其余可以;
在派生类外,由于基类的public成员也变成protected,就无法访问了,即派生类的对象无法访问任何基类的成员

公有继承

在派生类中,继承下来的成员的访问权限不变,所以派生类的对象可以访问基类的public成员





三种继承方式,一般都使用protected继承方式

  1. 对于私有继承,基类的protected成员在派生类中变成private,也还能访问;但派生链中,派生类的派生类就无法访问基类的任何成员,所以,一旦出现私有继承,类内直接访问的特性就无法在派生类中传递下去
  2. protected继承的优点是:既可以在本类中实现数据的隐藏(类内可以直接访问,类外不可被访问),又可以将其类内直接访问特性继续传递到派生类中(在派生类中可以直接访问,类外不可直接访问)





多重继承

class <派生类> : <继承方式> <基类1>, <继承方式> <基类2>, <继承方式> <基类3> ...
{
	···
};

基类成员初始化

构造函数

class B : protected A, protected C,protected E
{
	···
	public:
	B(args):E(arg1),C(arg2),A(arg3){ }
	
};

派生类构造函数执行顺序,先构造基类,再构造自身,而基类的构造顺序取决于继承顺序,而不是派生类构造函数的初始化列表顺序

析构函数
派生类的析构顺序和自身的构造顺序相反


基类的构造函数和析构函数不会被派生类继承,而只能通过派生类的构造函数或析构函数自动调用,完成对基类数据成员的初始化或清理工作





二义性和支配规则


二义性

在多重继承中,当在派生类中出现两个以上同名的可直接访问的基类成员时,便出现了二义性,也称之为访问冲突

class A{
	protected:
	int x;
	public:
	void show(){ cout<<x;}
	A(int n):x(n){}
};
class B{
	protected:
	int x;
	public:
	void show(){ cout<<x;}
	B(int n):x(n){}
};

class C:protected A,protected B{
	int c;
	public:
	void setA(int a) { x=a; }	//出现
	void setB(int b) { x=b; }	//二义性
	void setc(int c) { this->c=c; }
	C(int a,int b,int c):A(a),B(b){this->c = c;}
	C(){};
};
int main(){
	C c;
	c.setA(100);
	c.setB(200);
	c.show()   		//类外出现二义性

}

A类和B类具有同名的xshow(),他们均被继承到C类中,当C类对象访问时,就出现二义性。
解决方法:

  1. 改名
  2. 在派生类中使用作用域运算符

第1点明显不是个好办法
使用作用域运算符

void setA(int a){ A::x = a;}
void setB(int b){ B::x = b;}
c.A::show();
c.B::show();



在多层继承中,这种作用域运算符不允许嵌套使用

class D: protected C
{
	···
};

int main(){
	D d;
	d.setA(100);
	d.setB(200);
	d.C::B::show();   	//错误,直接d.B::show()即可
	d.C::A::show();		// d.A::show()
}

除了使用d.B::show()之外,还可以在D类中创建访问A类和B类成员x的函数

 void showA(){ cout<<A::x; }
 void showB(){ cout<<B::x; }

由于C++通过作用域运算符解决二义性,因此特别规定任一基类在派生类中只能被继承一次

class E:protected A,protected A			//错误
{
	···
};




支配规则

在C++中,允许派生类中新增加的成员名与其基类的成员名相同,而且这种同名不产生二义性。在派生类中访问同名成员时,若直接用成员名访问,则访问的是派生类自身的成员,只有使用作用域运算符,才能访问同名基类成员。这种优先关系称为支配规则。



所以一个类出现同名函数有两种情况

  1. 多重继承下,来自不同的基类的同名函数
  2. 派生类定义了和某个来自基类的函数同名的函数

前者产生二义性,后者不产生




虚基类

在一些多重继承当中,会出现如下情况

class A{};
class B:protected A{};
class C:protected A{};
class D:protected B,protected C{};

类D会有两份类A的成员,而在多重继承中,欲使这类公共基类在派生类中只能有一个拷贝,可以将基类A说明成虚基类。说明的方法是在派生类继承基类时,在继承方式前或后加上关键字virtual

class B:virtual protected A{};
class C:protected virtual A{};



对于派生类D的构造函数,先调用虚基类的构造函数,再调用非虚基类的构造函数





对象成员



class A
{	
	int a;
	
	public:
	A(int n):a(n){}
	void show(){ cout<<a<<endl; }

}

class B{
	A a1;			//对象成员
	int b;
	
	public:
	B(int a,int n):a1(a)   //这里只是类B的一个类A类型的成员变量a1
	{ b = n;}  			   //a1(a),对象a1调用构造函数进行初始化
	void show(){		   //而不是派生类的构造函数调用基类的构造函数
	a1.show();
	cout<<b;
	}
}

赋值兼容

可以将公有派生类的对象赋值给基类对象,反之不可

还允许

  1. 只有公有继承的派生类对象可以赋值给基类对象
  2. 公有继承的派生类对象在赋值给基类对象时,系统将派生类对象中从基类继承来的成员赋值给基类对象
  3. 公有继承下,可以将派生类对象的地址赋给基类的指针变量,Point *p = &line
  4. 公有继承下,派生类对象可以初始化基类的引用,Point &p = line

第2点,也是基类不能赋值给派生类的解释,派生类是基类的扩充,基类赋值给派生类会导致派生类扩充部分无值可赋

最后两种情况,通过基类指针或引用,只能访问派生类对象中继承来的成员,无法访问派生类扩充的成员

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值