C++学习笔记46——友元与继承

1,总的原则

继承层次中关于友元的总原则就是:友元关系不能继承。可以概述如下:
  1. 老爸的朋友不一定是我的朋友;
  2. 朋友的儿子不一定还是朋友;
  3. 由以上两条不难推出:老爸的朋友的儿子不一定还是朋友
但具体实施起来情况更为复杂,下面分别验证。

2,验证派生类不能继承基类的友元

// 类的定义
struct Frnd;//类的前置声明
class Baba
{
public:
	friend Frnd;//友元声明,还记得吗?	
protected:
	void test() { cout << "I am your father!\n" << endl; }
};

class Son2: public Baba
{
private:
	void son_test() { cout << "I am your son!\n" << endl; }
};

struct Frnd
{
	//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
	void check(Baba  &f) { f.test(); }
	void check2(Son2 &s) { s.test(); }
	//void check3(Son2 &s) { s.son_test(); }//error C2248: “Son2::son_test”: 无法访问 private 成员(在“Son2”类中声明)
};

//主函数
int main()
{
	Baba f;
	Son2 s;
	Frnd frd;

	//1,验证派生类不能继承父类的友元
	frd.check(f);// OK
	frd.check(s);// 居然也OK,使用基类的地方可以使用派生类
	frd.check2(s);// 居然还OK!
}
输出如下:


应当注意几点:
  1. 类Frnd的定义体内不能使用s.son_test()证明了基类的友元不再是派生类的友元;
  2. check2的定义体证明,派生类中从基类继承过来的成员还是对友元开放的
  3. main函数中frd.check(s)的用法,是在需要基类的地方使用public派生类。

3,验证友元、继承与动态绑定

// 结构体定义
struct Frnd;
class Baba
{
public:
	friend Frnd;
protected:
	virtual void test() { cout << "I am your father!\n" << endl; }
};

class Son2 : public Baba
{
private:
	virtual void test() { Baba::test(); cout << "son again!\n"; }
	void son_test() { cout << "I am your son!\n" << endl; }
};

struct Frnd
{
	//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
	void check(Baba  &f) { f.test(); }
	//void check2(Son2 &s) { s.test(); }//error C2248: “Son2::test”: 无法访问 private 成员(在“Son2”类中声明)
};

//主函数
int main()
{
	Baba f;
	Son2 s;
	Frnd frd;
	
	frd.check(f);// OK
	frd.check(s);// 居然也OK
}
输出结果:

这段代码最让人困惑的地方在于:Frnd中check2定义失败是由于它需要使用Son2::test(),但是在主函数中frd.check(s)借由动态绑定事实上也使用了Son2::test()——这一点可以由输出打印结果确认——但是却运行正确。就好像是用动态绑定骗过了编译器一样。
我们将友元与virtual函数的关系总结如下:
  1. 我们知道派生类中的成员分为3类:直接继承自基类的成员、重定义的虚函数和自己新增的成员;
  2. 基类的友元函数可以使用直接继承自基类的成员,不可以使用派生类新增的成员
  3. 对于派生类重定义的虚函数,基类的友元不能通过派生类的对象直接使用,因为这样必然导致使用的是派生类中的版本;但是可以通过动态绑定使用:即友元名义上用的是指向基类的指针或引用,但实际上该指针或引用指向了派生类的对象,友元中可以使用这种方法使用派生类的virtual成员,别且最终用到的还是派生类中的版本。
由以上3条我们可以推测:如果改变条件将动态绑定取消,比如讲check()改为通过值传递的版本,则check直接使用基类中的版本:
//其他地方不变,将check改为值传递版本
struct Frnd
{
	//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
	void check(Baba  f) { f.test(); }
	//void check2(Son2 &s) { s.test(); }//error C2248: “Son2::test”: 无法访问 private 成员(在“Son2”类中声明)
};
则输出结果如下:


两次使用的都是基类中的版本。

4,验证友元的派生类不再是友元

其他代码不变,增加如下定义:
//3,验证友元的派生类不再是友元
struct FrndSon:public Frnd
{
	//error C2248: “Baba::test”: 无法访问 protected 成员(在“Baba”类中声明)
	//void son_check(Baba  &f) { f.test(); }

};
主函数如下实现:
int main()
{
	Baba f;
	Son2 s;
	FrndSon fs;
	
	fs.check(f);
	fs.check(s);
}
输出结果与上一节一模一样:


总结如下:
  1. FrndSon中不能使用Baba中的protected成员,证明Frnd对Baba的友元关系没有继承给Frnd的派生类FrndSon;
  2. FrndSon的对象还是可以使用继承自Frnd的public成员函数check,而check()是Baba的友元,这种友元关系不变。
脑洞开一下,由于FrndSon是Frnd的派生类,那么FrndSon中的成员同样可以分成3类:直接继承自基类的成员、重定义的虚函数和自己新增的成员。直接继承自基类的成员(check)仍然是友元,自己新增的成员(son_check)不是友元已经验证过了。那么 重定义的虚函数还是不是友元呢?
struct Frnd
{
	//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
	void check(Baba  &f) { f.test(); }
	virtual void v_check(Baba  &f) { f.test(); }
	//void check2(Son2 &s) { s.test(); }//error C2248: “Son2::test”: 无法访问 private 成员(在“Son2”类中声明)
};

//3,验证友元的派生类不再是友元
struct FrndSon:public Frnd
{
	//error C2248: “Baba::test”: 无法访问 protected 成员(在“Baba”类中声明)
	//virtual void v_check(Baba  &f) { f.test(); }

	//error C2248: “Baba::test”: 无法访问 protected 成员(在“Baba”类中声明)
	//void son_check(Baba  &f) { f.test(); }

};

毫不意外,虚函数同样没有获得友元属性。
其实简单来说就是:友元的派生类(FrndSon)的类定义体中不能用到其他类(Baba)的protected或private成员。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值