C++基础#25:C++中的嵌套类(内部类)和外部类

本文详细介绍了C++中嵌套类的概念、作用域、访问权限等知识点,并通过多个实例展示了如何定义和使用嵌套类,包括如何通过内部类访问外部类的数据。

简介:

C++中,允许在一个类中定义另外一个类,在类中定义的类称为嵌套类(或内部类),定义嵌套类的类被称为外部类。定义嵌套类的目的在于隐藏类名,减少全局的标识符,从而限制用户能否使用该类建立对象。这样可以提高类的抽象能力,并且强调了两个类(外部类和嵌套类)之间的主从关系。

嵌套类的用法注意事项:

1)作用域:嵌套类被隐藏在外部类之中,该类名只能在外围类中使用。如果在外部类的作用域内使用该类名时(也就是外部类的外部),需要加名字限定,形如“外部类类名::内部类类名::内部类的成员函数名”;

2)访问权限:嵌套类名与它的外部类的对象成员名具有相同的访问权限规则。不能访问嵌套类的对象中的私有成员函数,也不能对外部类的私有部分中的嵌套类建立对象;

3)声明与定义:C++嵌套类必须声明在类的内部,但是可以定义在类的内部或者外部;在外部类之外定义一个嵌套类时,必须以外层类的名字限定嵌套类的名字;

4)嵌套类中说明的成员不是外围类中对象的成员,反之亦然。嵌套类的成员函数对外围类的成员没有访问权,反之亦然。国此,在分析嵌套类与外围类的成员访问关系时,往往把嵌套类看作非嵌套类来处理;

5)嵌套类是类封装演化出来的又一种类间关系,即其中组合或者聚集的成员不是产生于其他类,而是产生于嵌套类;

6)C++中,允许一个类继承另一个类的内部类,即内部类做为父类, 这时,必须用 “外部类类名::内部类类名”来访问内部类。

程序实例: 

例1: 定义一个简单的内部类:

分别“在内部类声明时定义内部类的成员函数”,和“在外部类之外进行内部类成员函数的实现”。

#include <iostream>
using namespace std;

class A {
public:
    int a;
    void fun1() {cout<<"A::fun1"<<endl;}

    class B{
        public:
            int b;
            void fun2() {cout<<"B::fun2"<<endl;}
            void fun3();
    };
};

//在外部类之外进行内部类的定义(实现),必须指明外部类,即外部类类名::内部类类名::内部类的成员函数名
void A::B::fun3() {
    cout<<"B::fun3"<<endl;
}


void test_fun() {

    A a;
    //B b; //error: unknown type name 'B'; did you mean 'A::B'?
    A::B b; //正确

    //b.fun1(); //error: no member named 'fun1' in 'A::B'; did you mean 'fun2'?
    b.fun2(); // 输出: B::fun2
    b.fun3(); // 输出: B::fun3

}


int main()
{
    
    test_fun();

    return 0;
}
/*
编译环境:mac os下用g++编译:
*/

具体代码解释,直接参考代码注释。

 例子2: 在外部继承另一个类的内部类:

C++中,允许一个类继承另一个类的内部类,即内部类做为父类, 这时,必须用 “外部类类名::内部类类名”来访问内部类。

对于例1中的代码,继续增加一个类D,继承自B,看如下的代码,

//error
 class D: public B { // error: unknown class name 'B'; did you mean 'A::B'?

 };

根据错误信息提示,很明显,编译器并不知道内部类B的存在。 

正确的代码如下:

//success
class D: public A::B { // error: unknown class name 'B'; did you mean 'A::B'?

};

void test_fun() {

    D d;

    //d.fun1(); //error: no member named 'fun1' in 'D'。类A与类D没有继承关系,类D的实例d不能访问A的成员函数和数据成员。
    d.fun2();   //输出:B::fun2。 继承自于父类B的fun2成员函数
    d.fun3();   //输出:B::fun3。 继承自于父类B的fun3成员函数
    

}

分析:

1)D继承自A的内部类B;

2)D的对象d无法访问A的成员函数,因为二者不具有继承关系; 

3)D的对象d可以访问B的成员函数,尽管B是A的内部类,因为D继承自B。 

例3: 如何通过内部类访问外部类的数据。

看下面的代码,

#include <iostream>
#define method_setOutMemer(classA, classB) \
    classA* pA = ((classA*)((char*)(this) - \
    offsetof(classA, m_x##classB))); \

using namespace std;

class A
{
private:
    int a;
public:
    //外部类的构造函数
    A(){a=0;}
    //输出函数
    void printA(){cout<<"A::printA, a="<<a<<endl;}

    //内部类
    class B
    {
    private:
        int b;    
    public:
        //内部类的构造函数
        B(){b=1;}
        //输出函数
        void printB(){cout<<"B::printB, b="<<b<<endl;}

        //访问外部类
        void setA()
        {
            method_setOutMemer(A,B);
            pA->a=100;
        }
    } m_xB;

public:
    
};

int main()
{
    A a;
    a.printA(); //输出:A::printA, a=0

    a.m_xB.setA(); //通过内部类的成员函数修改外部类的值
    a.printA(); //输出:A::printA, a=100

    return 0;
}

说明:

offsetof(A, m_x##B)获得的是m_xB在该内存块中与该内存块起始地址(这正是外部类实例的地址)的距离(偏移),即内部类this-外部类this的差值(以字节为单位)这样,用内部类this减去其自身的偏移,便可得到外部类实例a的指针pA。有了这个指针,就可以访问其对应的数据了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liranke

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值