zoukankan      html  css  js  c++  java
  • 【转】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)从作用域的角度来看,嵌套类与外围类是两个完全独立的类,只是主从关系,二者不能相互访问,也不存在友元关系。
    (2)从访问权限的角度来看,嵌套类既可为私有,也可为公有。在上面的例子中,嵌套类B的访问权限是public,可以在外围类的成员函数之外使用该嵌套类,使用时加上名字限定。如果将嵌套类B的访问权限设置为private,那么只能在外围类内使用。
    (3)嵌套类中的成员函数可以在它的类体外定义。
    (4)嵌套类可以访问外围类的静态成员变量,即使它的访问权限是私有的,访问方式通过”ClassName::staticVarName”来直接访问。
     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的转换。

  • 相关阅读:
    boost.property_tree的高级用法(你们没见过的操作)
    MFC- OnIdle空闲处理
    华为代码质量军规 (1) 数组访问,必须进行越界保护
    WinSocket 编程
    【C/C++】链表的理解与使用
    单链表
    C++ lambda表达式 (二)
    C++ lambda表达式 (一)
    C++11 volatile 类型
    关于结构体内存对齐方式的总结(#pragma pack()和alignas())
  • 原文地址:https://www.cnblogs.com/aheng123/p/5323846.html
Copyright © 2011-2022 走看看