C++知识点45——类继承中的类型转换与访问权限控制(上)

一、类的继承与类型转换

1.概述

一般情况,如果想把一个类的指针或引用绑定到另外一个对象上,需要指针或者引用的类型与指向对象一致。但是存在继承关系的类是个例外:可以将基类的指针或者引用指向子类

原因是因为:子类包含了父类非static成员和它本身的非static成员。因此,当一个基类的指针或引用指向子类对象时,所指向的真实对象有可能仅仅是父类,也有可能是完整的子类

上图中,指针或引用的类型叫静态类型,指针或引用指向的对象叫动态类型。静态类型在编译期就已经确定,而动态类型是不确定的(有可能是父类对象,也有可能是完整的子类),所以只能在运行时才能确定

当然,如果不适用指针或者引用,那么动态类型和静态类型永远一致,都是类名。

 

2.父子类的类型转换

正因为基类对象是完整的子类对象的一部分,而基类的指针或引用可以指向子类中基类的部分,所以,子类可以转化为基类,但是基类不能转化为子类,子类可以当做基类使用,而基类却不能当做子类使用。再简言之就是子类的中的成员基类没有,而基类的成员子类都有,所以基类不能转子类,子类能转基类

示例

class base1
{
public:
	base1(){}
	~base1(){}
	static void sfunc() {cout<<__func__<<endl;}
	static int st;
};

int base1::st=10;

class derive2:public base1
{
public:
	derive2(){cout<<__func__<<endl;}
	~derive2(){cout<<__func__<<endl;}
};

int main(int argc, char const *argv[])
{
	derive2 d2;
	base1 b1=d2;
	base1 b1t;
	b1t=d2;

	derive2 t=b1;
	return 0;
}

基类在给子类初始化时,需要转化为子类,又因为基类不能转子类,所以报错

无论是用子类对象给基类对象赋值,还是用子类对象给基类对象初始化,调用的都是基类的拷贝构造或者基类的operator=,而拷贝构造和operator=的形参是const 对象的引用,而基类的引用可以指向子类对象,所以可以将子类对象作为实参,而在基类的拷贝构造或者operator=中,只能对基类中的成员进行初始化或者赋值,无法处理子类中的成员,所以当使用子类对象对基类对象进行初始化时,除基类外的子类部分被切掉了简言之就是基类中没有子类的成员,所以除基类外的子类部分被切掉了

 

此外,子类的指针或引用也不能指向基类。即使一个基类的指针或引用绑定在子类上,也不能让子类的指针或引用指向基类

示例1

int main(int argc, char const *argv[])
{
	base1 b1;
	derive2 *p=&b1;
	return 0;
}

如果子类的指针可以指向基类,那么就可以通过子类的指针访问基类中原来没有的成员,所以,不允许子类的指针或引用指向基类对象

 

示例2

int main(int argc, char const *argv[])
{
	base1 *pb=new derive2();
	derive2 *p=pb;
	return 0;
}

虽然基类的指针指向了一个子类,并且子类指针也指向了该子类对象,但是编译器在编译derive2 *p=pb;时发现:有子类的指针指向基类,于是编译器认为违反了子类的指针或引用不能指向基类的原则,所以报错

总之,不能用子类的指针或者引用指向基类

 

二、protected关键字的其他性质

protected是专为继承而生的,基类中的protected成员对于除子类的其他类和作用域来说是不可访问的;对于子类的成员和友元是可以访问的

注意:在子类中访问父类的protected成员时,只能通过子类对象访问,不能通过父类对象访问

示例

class base3
{
public:
	base3(){cout<<__func__<<endl;}
	~base3(){cout<<__func__<<endl;}
protected:
	int i;
};

class derive3:public base3
{
public:
	derive3(){cout<<__func__<<endl;}
	~derive3(){cout<<__func__<<endl;}
	//void func(base3 &b) {b.i=3;} //需要注释掉
	void func2(derive3 &d) {d.i=3;}
};

上述代码的第15行如果不注释掉,会报错,因为在子类中访问父类的protected成员时,只能通过子类对象访问,不能通过父类对象访问

 

三、类的继承与友元

如果一个类的可以做基类,那么,当该类被继承时,基类中的友元不会被继承,不能成为子类的友元,所以基类的友元不能访问子类对象的成员。同样,子类中的友元也不能访问基类中的protected和private成员。简言之,子类和基类中的友元没有任何关系

示例

class base4
{
	friend class friendclass;
public:
	base4(){cout<<__func__<<endl;}
	~base4(){cout<<__func__<<endl;}
	
protected:
	int i1;

private:
	int i2;
};

class derive4:public base4
{
friend void setfunc(derive4 &b);
friend void setfunc(base4 &b);
public:
	derive4(){cout<<__func__<<endl;}
	~derive4(){cout<<__func__<<endl;}

protected:
	int i3;

private:
	int i4;
};

class friendclass {
public:
	friendclass() {cout<<__func__<<endl;}
	~friendclass() {cout<<__func__<<endl;}

	void set(derive4 &d) {d.i1=10;d.i2=10;d.i3=10;d.i4=10;}
};

void setfunc(derive4 &d) {
	d.i1=10;d.i2=10;d.i3=10;d.i4=10;
}

void setfunc(base4 &d) {
	d.i1=10;d.i2=10;
}

上述代码有编译无法通过的原因:

1、friendclass是base4的友元而不是derive的友元,所以friendclass中的set函数无法访问子类derive中的成员(i3, i4)

2、void setfunc(derive4 &b);是子类的友元,但是不是基类的友元,所以无法访问基类中的private成员(i2)。但是由于derive4是base4的子类,所以可以通过子类访问基类中的protected成员i1

3、friend void setfunc(base4 &b);虽然是子类derive的友元,但是参数是基类base4的引用,所以无法在子类中通过基类base4访问基类的protected成员i1,更无法访问基类的private成员i2

 

参考

《C++ Primer》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值