C++之内部类(嵌套类)与外部类及友元

本人能力、精力有限,所言所感都基于自身的实践和有限的阅读、查阅,如有错误,欢迎拍砖,敬请赐教——博客园:钱智慧。

先上代码:

 1 class Outer
 2 {
 3 public:
 4     Outer(){m_outerInt=0;}
 5 private:
 6     int m_outerInt;
 7 public:
 8     //内部类定义开始
 9     class Inner
10     {
11     public:
12         Inner(){m_innerInt=1;}
13     private:
14         int m_innerInt;
15     public:
16         void DisplayIn(){cout<<m_innerInt<<endl;}
17     } ;
18     //End内部类
19 public:
20     void DisplayOut(){cout<<m_outerInt<<endl;}
21 };
22 
23 int main()
24 {
25     Outer out;
26     Outer::Inner in;
27     out.DisplayOut();
28     in.DisplayIn();
29 
30     return 0;
31 }
View Code

如上面代码所示,这种情况下,外部类与内部类其实联系并不大,外部类无非仅仅限定了内部类类名的作用域范围,完全可以加上Outer限定之后像使用任何其他类一样来使用内部类,Outer于Inner而言仅仅是一种命名空间。

提问:上面代码中,内部类(Inner)成员函数(比如DisplayIn)如何访问外部类(Outer)数据成员呢?

答:问这个问题之前,先要明白一个事实:将来你是在一个Inner实例对象上调用Inner的成员函数的,而所谓的“访问外部类数据成员”这种说法是不合理的,“外部类”及任何类,只是代码而已,是一种说明,从内存的角度来讲,程序运行起来之后,代码存储在代码区,所以应该问“如何访问外部类实例的数据成员”,如此,你得先有一个外部类实例(或者实例的指针),然后才能谈访问。

退一步讲,如果你不管三七二十一,直接在Inner的DisplayIn方法里加上这样一行:

1 m_outerInt=10;
View Code

然后你编译、链接也都通过了(事实上这是不可能的),那么,在main函数中:

1 int main()
2 {
3     Outer::Inner in;
4     in.DisplayIn();
5 
6     return 0;
7 }
View Code

如果这样你都能正常运行,天理何在?DisplayIn中的m_outerInt到底是哪个实例的数据?

所以,为了避免这样荒唐的事情发生,语法层面就已经使得上述不可能发生:连编译都不会通过。

提问:把上面代码中的Inner设置为Outer的友元类之后,能解决问题吗?


答:该提问者都不仅犯了第一个提问者的错误,还误解了友元的含义。

友元举例:

 1 class Inner;
 2 
 3 class Outer
 4 {
 5 public:
 6     Outer(){m_outerInt=0;}
 7 private:
 8     int m_outerInt;
 9 public:
10     /*//内部类定义开始
11     class Inner
12     {
13     public:
14         Inner(){m_innerInt=1;}
15     private:
16         int m_innerInt;
17     public:
18         void DisplayIn(){cout<<m_innerInt<<endl;}
19     } ;
20     //End内部类*/
21 public:
22     void DisplayOut(){cout<<m_outerInt<<endl;}
23     friend Inner;
24 };
25 class Inner
26 {
27 public:
28     Inner(){m_innerInt=1;}
29 private:
30     int m_innerInt;
31 public:
32     void DisplayIn(){cout<<m_innerInt<<endl;}
33     //友元影响的函数
34     void TestFriend(Outer out)
35     {
36         cout<<"Good Friend:"<<out.m_outerInt<<endl;
37     }
38 } ;
39 
40 int main()
41 {
42     Outer out;
43     out.DisplayOut();
44     Inner in;
45     in.DisplayIn();
46     in.TestFriend(out);
47     return 0;
48 }
View Code

内部类如果想达到友元访问效果(直接通过实例或者实例指针来访问实例的非公有成员),是不需要另外再声明为friend的,原因不言自明:都已经是自己人了。

提问:内部类实例(作为外部类的数据成员)如何访问外部类实例的成员呢?

见如下代码:

 1 #include <iostream>
 2 #define METHOD_PROLOGUE(theClass, localClass) \
 3     theClass* pThis = ((theClass*)((char*)(this) - \
 4     offsetof(theClass, m_x##localClass))); \
 5 
 6 using namespace std;
 7 
 8 class Outer
 9 {
10 public:
11     Outer(){m_outerInt=0;}
12 private:
13     int m_outerInt;
14 public:
15     //内部类定义开始
16     class Inner
17     {
18     public:
19         Inner(){m_innerInt=1;}
20     private:
21         int m_innerInt;
22     public:
23         void DisplayIn(){cout<<m_innerInt<<endl;}
24         // 在此函数中访问外部类实例数据
25         void setOut()
26         {
27             METHOD_PROLOGUE(Outer,Inner);
28             pThis->m_outerInt=10;
29         }
30     } m_xInner;
31     //End内部类
32 public:
33     void DisplayOut(){cout<<m_outerInt<<endl;}
34 };
35 
36 int main()
37 {
38     Outer out;
39     out.DisplayOut();
40     out.m_xInner.setOut();
41     out.DisplayOut();
42     return 0;
43 }
View Code

看main函数:程序执行完main函数第一句后,内存中便有了一个数据块,它存储着out的数据,而m_xInner也在数据块中,当然,&out和this指针(外部类)都指向该内存块的起始位置,而内部类代码中的this指针当然就指向m_xInner的起始内存了,offsetof(theClass, m_x##localClass)获得的便是m_xInner在该内存块中与该内存块起始地址(这正是out的地址)的距离(偏移),即内部类this-外部类this的差值(以字节为单位)这样,用内部类this减去其自身的偏移,便可得到pThis。有了out的地址,基本上可以对其为所欲为了,至于为何要有char*强转,可以go to definition of offsetof,可以看到其实现中有个关于char的转换。

转载于:https://www.cnblogs.com/qzhforthelife/p/3226885.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值