[c++11]成员访问控制

参考:Member Access Control (C++)

类的访问控制能够有效分离公共接口和私有实现细节,以及派生类可访问成员

访问类型

3种访问类型:

  1. private:私有函数,仅对类的成员函数和友元(friend)函数可见
  2. protected:受保护函数,除了对类的成员函数和友元函数可见外,还对派生类可见
  3. public:公共函数,没有访问限制

class默认的访问权限是privatestructunion默认的访问权限是public

派生类的访问控制

对于派生类的成员来说,其对于基类成员的访问权限取决于两个因素:

  • 是否派生类使用public访问说明符声明基类
  • 基类成员的访问权限

下图显示了派生类对于基类成员的访问控制

  • 对于基类中声明为private的成员而言,派生类均不能访问
  • 对于基类中声明为protected的成员而言
    • 如果派生类使用private声明基类,可看成派生类中的private成员
    • 如果派生类使用protected声明基类,可看成派生类的protected成员
    • 如果派生类使用public声明基类,可看成派生类的protected成员
  • 对于基类中声明为public的成员而言
    • 如果派生类使用private声明基类,可看成派生类的private成员
    • 如果派生类使用protected声明基类,可看成派生类的protected成员
    • 如果派生类使用public声明基类,可看成派生类的public成员

如果没有显式声明基类访问符,

  • 对于class而言,默认是private声明
  • 对于struct而言,默认是public声明

virtual函数

virtual函数的使用不影响访问控制。示例如下:

class Base {
public:
    virtual int show();
};

int Base::show() {
    return 1;
}

class Drived : public Base {
private:
    int show() override;
};

int Drived::show() {
//    Base::show();
    return 2;
}

int main() {
    Drived drived;
    drived.show(); // error

    Base base;
    base.show(); // correct
}

基类中的virtual函数是public访问权限,所以可以被对象调用;而派生类中的重写函数是private访问权限,所以不能被调用

多继承访问控制

c++支持多继承,就可能会发生派生类经过多个路径抵达同一基类,如下图所示

因为可以沿着这些不同的路径应用不同的访问控制,所以编译器选择提供最大访问权限的路径

上图中通过RightPath能够更多的访问基类VBase

友元

参考:friend (C++)

友元(friend)的作用是单独为外部或派生类提供访问权限

友元声明

c++11开始,有两种声明友元类的方式:

friend class F;
friend F;
  • 如果在最内部的命名空间中找不到同名类,第一种方式将引入新的类F
  • 第二种方式不介绍新的类,它在类声明后使用,或者在将模板类型参数或typedef声明为友元时使用
namespace NS
{
    class M
    {
        class friend F;  // 未声明类F时使用第一种方式
        friend F;        // error C2433: 'NS::F': 'friend' not permitted on data declarations
    };
}

// 声明模板参数为friend
template <typename T>
class my_class
{
    friend T;
    //...
};

// 声明typedef重命名类为friend
class Foo {};
typedef Foo F;

class G
{
    friend F; // OK
    friend class F // Error C2371 -- redefinition
};

如果要声明两个类互为友元,则必须将第二个类声明为第一个类的友元。同时可以选择第一个类中哪些函数是第二个类的友元

友元函数

友元函数不是类的成员,不能使用成员选择算子(.或者->)来访问它(除非它是另一个类的函数),但是被授予权限访问类的privateprotected成员

友元函数声明可以放置在类声明的任意位置,不影响其访问权限。示例如下:

class Point {
    friend void ChangePrivate(Point &);

public:
    Point(void) : m_i(0) {}

    void PrintPrivate(void) { cout << m_i << endl; }

private:
    int m_i;
};

void ChangePrivate(Point &i) { i.m_i++; }

int main() {
    Point sPoint;
    sPoint.PrintPrivate();
    ChangePrivate(sPoint);
    sPoint.PrintPrivate();
// Output: 0
//         1
}

创建友元函数ChangePrivate和类Point,友元函数能够改变类Point的私有变量m_i

友元类

友元类是指其整个类的成员函数均是另一个类的友元函数。示例如下:

class YourClass {
    friend class YourOtherClass;  // Declare a friend class
public:
    YourClass() : topSecret(0) {}

    void printMember() { cout << topSecret << endl; }

private:
    int topSecret;
};

class YourOtherClass {
public:
    void change(YourClass &yc, int x) { yc.topSecret = x; }
};

int main() {
    YourClass yc1;
    YourOtherClass yoc1;
    yc1.printMember();
    yoc1.change(yc1, 5);
    yc1.printMember();
}

YourOtherClass是类YourClass的友元类,所以YourOtherClass的所有函数均能访问类YouClass的私有成员

注意 1:友元关系无法继承(inherited),所以YourOtherClass的派生类无法访问YourClass

注意 2:友元关系无法传递(transitive),所以YourOtherClass的友元类无法访问YourClass

内联友元函数

友元函数可以在类声明内定义(给定函数体)。这些函数是内联函数,与成员内联函数一样,其作用域在整个类内