【转】C++之内部类(嵌套类)与外部类及友元
【转】http://baike.baidu.com/link?url=Md223wQoT5s-3cZ5xRnj1pGmvm310DKAuh-HDrcEdc2l24rwobHrdEc_Mi4Z3BGP0jxRqTCBQkRXJoGtuWNS7_
【转载】http://www.cnblogs.com/qzhforthelife/archive/2013/07/31/3226885.html
1.1嵌套类的定义
1.2嵌套类的作用
1.3嵌套类的使用示例
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 class B 8 { 9 public: 10 B(char* name){ 11 cout<<"constructing B:"<<name<<endl; 12 } 13 void printB(); 14 }; 15 B b; 16 A():b("In class A"){ //A的构造函数 17 cout<<"constructing A"<<endl; 18 } 19 }; 20 21 void A::B::printB(){ 22 cout<<"B's member function"<<endl; 23 } 24 25 int main(int argc,char* argv[]) 26 { 27 A a; 28 A::B b("outside of A"); 29 b.printB(); 30 }
1 constructing B:In class A 2 constructing A 3 constructing B:outside of A 4 B's member function
1 #include<iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 A(){ 8 cout << "A construct" <<endl; 9 } 10 class B{ 11 public: 12 B(){ 13 cout << "B construct" <<endl; 14 } 15 }; 16 }; 17 18 int main() 19 { 20 A ca; 21 cout<<"-------------"<<endl; 22 A::B b; 23 return 0; 24 }
程序输出结果:
1 A construct 2 ------------- 3 B construct
可以看到,在进行创建一个外围类的对象时,只执行外围类的构造函数,而并不会先执行嵌套类的构造函数然后再执行外围类的构造函数。嵌套类与继承或成员对象是不同的。
【转载】http://www.cnblogs.com/qzhforthelife/archive/2013/07/31/3226885.html
先上代码:
1 class Outer 2 { 3 private: 4 int m_outerInt; 5 public: 6 Outer(){m_outerInt=0;} 7 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 void DisplayOut(){cout<<m_outerInt<<endl;} 20 }; 21 22 int main() 23 { 24 Outer out; 25 Outer::Inner in; 26 out.DisplayOut(); 27 in.DisplayIn(); 28 29 return 0; 30 }
如上面代码所示,这种情况下,外部类与内部类其实联系并不大,外部类无非仅仅限定了内部类类名的作用域范围,完全可以加上Outer限定之后像使用任何其他类一样来使用内部类,Outer于Inner而言仅仅是一种命名空间。
提问:上面代码中,内部类(Inner)成员函数(比如DisplayIn)如何访问外部类(Outer)数据成员呢?
答:问这个问题之前,先要明白一个事实:将来你是在一个Inner实例对象上调用Inner的成员函数的,而所谓的“访问外部类数据成员”这种说法是不合理的,“外部类”及任何类,只是代码而已,是一种说明,从内存的角度来讲,程序运行起来之后,代码存储在代码区,所以应该问“如何访问外部类实例的数据成员”(数据成员针对的是每个对象而言的),如此,你得先有一个外部类实例(或者实例的指针),然后才能谈访问。
退一步讲,如果你不管三七二十一,直接在Inner的DisplayIn方法里加上这样一行:
1 m_outerInt=10;
然后你编译、链接也都通过了(事实上这是不可能的),那么,在main函数中:
1 int main() 2 { 3 Outer::Inner in; 4 in.DisplayIn(); 5 6 return 0; 7 }
如果这样你都能正常运行,天理何在?DisplayIn中的m_outerInt到底是哪个实例的数据?
所以,为了避免这样荒唐的事情发生,语法层面就已经使得上述不可能发生:连编译都不会通过。
提问:把上面代码中的Inner设置为Outer的友元类之后,能解决问题吗?
答:该提问者不仅犯了第一个提问者的错误,还误解了友元的含义。
友元举例:
1 class Inner; 2 3 class Outer 4 { 5 private: 6 int m_outerInt; 7 public: 8 Outer(){m_outerInt=0;} 9 void DisplayOut(){cout<<m_outerInt<<endl;} 10 friend class Inner; 11 }; 12 13 class Inner 14 { 15 private: 16 int m_innerInt; 17 public: 18 Inner(){m_innerInt=1;} 19 void DisplayIn(){cout<<m_innerInt<<endl;} 20 //友元影响的函数 21 void TestFriend(Outer out) 22 { 23 cout<<"Good Friend:"<<out.m_outerInt<<endl; 24 } 25 } ; 26 27 int main() 28 { 29 Outer out; 30 out.DisplayOut(); 31 Inner in; 32 in.DisplayIn(); 33 in.TestFriend(out); 34 return 0; 35 }
经过以上说明后,类Inner的所有成员函数都是类Outer的友元函数,能存取类Outer的私有成员和保护成员。
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
(4) 友元函数并不是类的成员函数,因此在类外定义的时候不能加上class::function name
(5) 友元函数不能直接访问类中的私有成员只能通过类的对象来访问 私有成员 ,
class A{
int a;
pulbic:
friend void g();
}
则void g(){a=2;}就是错误的
void g(){A m; m.a=2;}就是正确的
(6) friend 出现的位置对友元来说无关紧要。即把友元声明在公有私有和受保护的位置都是一样的。
内部类如果想达到友元访问效果(直接通过实例或者实例指针来访问实例的非公有成员),是不需要另外再声明为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 private: 11 int m_outerInt; 12 public: 13 Outer(){m_outerInt=0;} 14 //内部类定义开始 15 class Inner 16 { 17 private: 18 int m_innerInt; 19 public: 20 Inner(){m_innerInt=1;} 21 22 void DisplayIn(){cout<<m_innerInt<<endl;} 23 // 在此函数中访问外部类实例数据 24 void setOut() 25 { 26 METHOD_PROLOGUE(Outer,Inner); 27 pThis->m_outerInt=10; 28 } 29 } m_xInner; 30 //End内部类 31 32 void DisplayOut(){cout<<m_outerInt<<endl;} 33 }; 34 35 int main() 36 { 37 Outer out; 38 out.DisplayOut(); 39 out.m_xInner.setOut(); 40 out.DisplayOut(); 41 return 0; 42 }
看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的转换。