• 访问控制和封装、友元


    在C++语言中,我们使用访问控制说明符加强类的封装性:

    • 定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口
    • 定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private部分封装了(即隐藏了)类的实现细节。

    使用class或struct关键字

    我们使用class关键字而非struct开始类的定义。这种变化仅仅是形式上有所不同,实际上我们可以使用者两个关键字中的任何一个定义类。唯一的一个区别是,struct和class的默认访问权限不太一样。

    类可以在它的第一个访问说明符之前定义成员,对这种成员的访问权限依赖于类定义的方式。如果我们使用关键字struct,则定义的第一个访问说明符之前的成员时public的;相反,如果我们使用class关键字,则这些成员时private的。

    使用class和struct定义类唯一的区别就是默认的访问权限

    友元

    类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元。如果类想把一个函数作为它的友元,只需要增加一条以friend关键字开始的函数声明语句即可:

    友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。友元不是类的成员也不受它所在区域访问控制级别的约束。

    一般来说,最好在类的定义开始或类结束的位置集中声明友元

    友元的声明

    友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明

    为了是友元对类的用户可见,我们通常把友元的声明与类本身放置在同一头文件中(类的外部)

    许多编译器并没有限定友元函数必须在使用之前在类的外部声明。

    友元再探

    我们的Sales_data类把三个普通的非成员函数定义成了友元。类还可以把其他的类定义成友元,也可以把其他类(之前已定义过的)的成员函数定义成友元。此外,友元函数能定义在类的内部,这样的函数是隐式内联的。

    类之间的友元关系

    class Screen{
        //Window_mgr的成员可以访问Screen类的私有成员
        friend class Window_mgr;
        //Screen类的剩余部分
    };

    如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。通过上面的声明,Window_mgr被指定为Screen的友元,因此我们可以将Window_mgr的clear成员写成如下的形式:

    class Window_mgr{
    public:
        //窗口中每个屏幕的编号
        using ScreenIndex=vector<Screen>::size_type;
        //按照编号将指定的Screen重置为空白
        void clear(ScreenIndex);
    privata:
        vector<Screen> screens{Screen(24,80,' ')};
    };
    
    void Window_mgr::clear(ScreenIndex i)
    {
        //s是一个Screen引用,指向我们想清空的那屏幕
        Screen &s=screens[i];
        //将那个选定的Screen重置为空白
        s.contents=string(s.height*s.width,' ');
    
    }

    一开始,首先把s定义成screen是 vector中第i个位置上的Screen引用,随后利用Screen的height和width成员计算出一个string对象,并令其含有若干个空白字符,最好我们把这个含有很多空白的字符串赋给contents成员。

    如果clear不是Screen的友元,上面的代码将无法通过编译,因为clear将不能放弃Screen的height、width和contents成员。而当Screen将Window_mgr指定为其友元之后,Screen的所有成员对于Window_mgr都变成可见的了。

    必须注意一点的是,友元关系不存在传递性。也就是说,如果Window_mgr有它自己的友元,则这些友元并不能理所当然地具有访问Screen的特权。

    每个类负责控制自己的友元类或友元函数。

    令成员函数作为友元

    除了令整个Window_mgr作为友元之外,Screen还可以只为clear提供访问权限。当把一个成员函数声明成友元时,我们必须明确指出该成员函数属于哪个类:

    class Screen{
        //Window_mgr::clear必须在Screen类之前被声明
        friend void Window_mgr::clear(ScreenIndex);
        //Screen类的剩余部分
    };

    要想令某个成员函数作为友元,我们必须仔细阻止程序的结构以满足声明和定义的彼此依赖关系。在这个例子中,我们必须按照如下方式设计程序:

    • 首先定义Window_mgr类,其中声明clear函数,但是不能定义它。在clear使用Screen的成员之前必须先声明Screen。
    • 接下来定义Screen,包括对于clear的友元声明
    • 最后定义clear,此时它才可以使用Screen的成员。

    函数重载和友元

    尽管重载函数的名字相同,但他们仍然是不同的函数。因此,如果一个类想把一组重载函数声明成它的友元,它需要对这组函数中的每一个分别声明:

    //重载的storeOn函数

    extern ostream& storeOn(ostream& ,Screen &);
    extern BitMap &storeOn(BitMap& ,Screen &);
    class Screen{
        //StoreOnde ostream版本能访问Screen对象的私有部分
        friend ostream::ostream& storeOn(ostream& ,Screen);
    };

    Screen类把接受ostream&的storeOn函数声明成它的友元,但是接受BitMap&作为参数的版本仍然不能访问Screen。

    友元声明和作用域

    类和非成员函数的声明不是必须在它们的友元声明之前。当一个名字第一次出现在一个友元声明中时,我们隐式地假定该名字在当前作用域中是可见的。然而,友元本身不一定真的声明在当前作用域中。

    甚至就算在类的内部定义该函数,我们也必须在类的外部提供相应的声明从而使得函数可见。换句话说,即使我们仅仅是用声明友元的类的成员调用该友元函数,它也必须是被声明过的:

    struct X{
        friend void f() { /*友元函数可以定义在类的内部*/}
        X() {f();}  //错误:f还没有被声明
        void g();
        void h();
    };
    void X::g(){return f();}  //错误:f还没有被声明
    void f();   //声明那个定义在X中的函数
    void X::h() {return f();}  //正确:现在f的声明在作用域中了
  • 相关阅读:
    HDU1272---(并查集)简单应用
    HDU1232 畅通工程---(经典并查集应用)
    HDU 1877 又一版 A+B(进制转换)
    L1-020. 帅到没朋友
    L2-001. 紧急救援---(Dijkstra,记录路径)
    JVM Class字节码之三-使用BCEL改变类属性
    JVM Class详解之一
    Jvm原理剖析与调优之内存结构
    虚拟化的发展历程和实现原理——图文详解
    JVM学习笔记(四)------内存调优
  • 原文地址:https://www.cnblogs.com/wuchanming/p/3900033.html
Copyright © 2020-2023  润新知