转载于:http://www.cnblogs.com/qzhforthelife/p/3226885.html
先上代码:
class Outer { public: Outer(){m_outerInt=0;} private: int m_outerInt; public: //内部类定义开始 class Inner { public: Inner(){m_innerInt=1;} private: int m_innerInt; public: void DisplayIn(){cout<<m_innerInt<<endl;} } ; //End内部类 public: void DisplayOut(){cout<<m_outerInt<<endl;} }; int main() { Outer out; Outer::Inner in; out.DisplayOut(); in.DisplayIn(); return 0; }
如上面代码所示,这种情况下,外部类与内部类其实联系并不大,外部类无非仅仅限定了内部类类名的作用域范围,完全可以加上Outer限定之后像使用任何其他类一样来使用内部类,Outer于Inner而言仅仅是一种命名空间。
提问:上面代码中,内部类(Inner)成员函数(比如DisplayIn)如何访问外部类(Outer)数据成员呢?
答:问这个问题之前,先要明白一个事实:将来你是在一个Inner实例对象上调用Inner的成员函数的,而所谓的“访问外部类数据成员”这种说法是不合理的,“外部类”及任何类,只是代码而已,是一种说明,从内存的角度来讲,程序运行起来之后,代码存储在代码区,所以应该问“如何访问外部类实例的数据成员”,如此,你得先有一个外部类实例(或者实例的指针),然后才能谈访问。
退一步讲,如果你不管三七二十一,直接在Inner的DisplayIn方法里加上这样一行:
1 m_outerInt=10;
然后你编译、链接也都通过了(事实上这是不可能的),那么,在main函数中:
int main() { Outer::Inner in; in.DisplayIn(); return 0; }
如果这样你都能正常运行,天理何在?DisplayIn中的m_outerInt到底是哪个实例的数据?
所以,为了避免这样荒唐的事情发生,语法层面就已经使得上述不可能发生:连编译都不会通过。
提问:把上面代码中的Inner设置为Outer的友元类之后,能解决问题吗?
答:该提问者都不仅犯了第一个提问者的错误,还误解了友元的含义。
友元举例:
class Inner; class Outer { public: Outer(){m_outerInt=0;} private: int m_outerInt; public: /*//内部类定义开始 class Inner { public: Inner(){m_innerInt=1;} private: int m_innerInt; public: void DisplayIn(){cout<<m_innerInt<<endl;} } ; //End内部类*/ public: void DisplayOut(){cout<<m_outerInt<<endl;} friend Inner; }; class Inner { public: Inner(){m_innerInt=1;} private: int m_innerInt; public: void DisplayIn(){cout<<m_innerInt<<endl;} //友元影响的函数 void TestFriend(Outer out) { cout<<"Good Friend:"<<out.m_outerInt<<endl; } } ; int main() { Outer out; out.DisplayOut(); Inner in; in.DisplayIn(); in.TestFriend(out); return 0; }
内部类如果想达到友元访问效果(直接通过实例或者实例指针来访问实例的非公有成员),是不需要另外再声明为friend的,原因不言自明:都已经是自己人了。
提问:内部类实例(作为外部类的数据成员)如何访问外部类实例的成员呢?
见如下代码:
#include <iostream> #define METHOD_PROLOGUE(theClass, localClass) theClass* pThis = ((theClass*)((char*)(this) - offsetof(theClass, m_x##localClass))); using namespace std; class Outer { public: Outer(){m_outerInt=0;} private: int m_outerInt; public: //内部类定义开始 class Inner { public: Inner(){m_innerInt=1;} private: int m_innerInt; public: void DisplayIn(){cout<<m_innerInt<<endl;} // 在此函数中访问外部类实例数据 void setOut() { METHOD_PROLOGUE(Outer,Inner); pThis->m_outerInt=10; } } m_xInner; //End内部类 public: void DisplayOut(){cout<<m_outerInt<<endl;} }; int main() { Outer out; out.DisplayOut(); out.m_xInner.setOut(); out.DisplayOut(); return 0; }
看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的转换。