C++ 访问控制详解(public、private、protected)

一、访问控制基础概念

1.1 三种访问级别概述

class AccessLevels {
private:
    // 私有成员:只能被本类的成员函数和友元访问
    int privateData;
    void privateMethod() {
        std::cout << "Private method\n";
    }
    
protected:
    // 保护成员:能被本类、派生类的成员函数和友元访问
    int protectedData;
    void protectedMethod() {
        std::cout << "Protected method\n";
    }
    
public:
    // 公有成员:能被任何代码访问
    int publicData;
    void publicMethod() {
        std::cout << "Public method\n";
    }
    
    // 演示内部访问
    void demonstrateInternalAccess() {
        privateData = 1;      // ✓ 类内部可以访问private
        protectedData = 2;    // ✓ 类内部可以访问protected
        publicData = 3;       // ✓ 类内部可以访问public
        
        privateMethod();      // ✓
        protectedMethod();    // ✓
        publicMethod();       // ✓
    }
};
 
// 外部访问演示
void externalAccess() {
    AccessLevels obj;
    
    // obj.privateData = 1;   // ✗ 错误:不能访问private
    // obj.privateMethod();    // ✗ 错误:不能访问private
    
    // obj.protectedData = 2; // ✗ 错误:不能访问protected
    // obj.protectedMethod(); // ✗ 错误:不能访问protected
    
    obj.publicData = 3;       // ✓ 可以访问public
    obj.publicMethod();       // ✓ 可以访问public
}

1.2 class vs struct 的默认访问级别

// class 默认是 private
class MyClass {
    int defaultPrivate;      // 默认 private
    
public:
    int explicitPublic;      // 显式 public
};
 
// struct 默认是 public
struct MyStruct {
    int defaultPublic;        // 默认 public
    
private:
    int explicitPrivate;      // 显式 private
};
 
// union 默认是 public
union MyUnion {
    int defaultPublic;        // 默认 public
    float anotherPublic;
    
private:
    double explicitPrivate;   // 显式 private
};

二、继承中的访问控制

2.1 继承方式对访问级别的影响

class Base {
private:
    int privateBase = 1;
protected:
    int protectedBase = 2;
public:
    int publicBase = 3;
    
    void showBase() {
        std::cout << "Base: " << privateBase << ", " 
                  << protectedBase << ", " << publicBase << "\n";
    }
};
 
// 公有继承:保持原有访问级别
class PublicDerived : public Base {
public:
    void accessMembers() {
        // privateBase = 10;     // ✗ 不能访问基类的private
        protectedBase = 20;      // ✓ protected在派生类中仍是protected
        publicBase = 30;         // ✓ public在派生类中仍是public
    }
};
 
// 保护继承:public变protected,protected保持,private不可访问
class ProtectedDerived : protected Base {
public:
    void accessMembers() {
        // privateBase = 10;     // ✗ 不能访问
        protectedBase = 20;      // ✓ protected保持protected
        publicBase = 30;         // ✓ public变为protected
    }
    
    // 使用using声明改变访问级别
    using Base::publicBase;      // 将publicBase重新声明为public
};
 
// 私有继承:public和protected都变private
class PrivateDerived : private Base {
public:
    void accessMembers() {
        // privateBase = 10;     // ✗ 不能访问
        protectedBase = 20;      // ✓ protected变为private
        publicBase = 30;         // ✓ public变为private
    }
    
    // 使用using声明选择性地公开某些成员
    using Base::showBase;        // 将showBase重新声明为public
};
 
// 访问级别测试
void testInheritance() {
    PublicDerived pub;
    pub.publicBase = 100;        // ✓ public继承保持public
    // pub.protectedBase = 200;  // ✗ protected不能外部访问
    
    ProtectedDerived prot;
    // prot.publicBase = 100;    // ✗ protected继承后变为protected
    
    PrivateDerived priv;
    // priv.publicBase = 100;    // ✗ private继承后变为private
    priv.showBase();             // ✓ 通过using声明公开了
}

2.2 多级继承中的访问控制

class GrandParent {
protected:
    int grandParentProtected = 1;
public:
    int grandParentPublic = 2;
};
 
class Parent : protected GrandParent {
protected:
    int parentProtected = 3;
public:
    int parentPublic = 4;
    // GrandParent的public成员在这里变成了protected
};
 
class Child : public Parent {
public:
    void accessMembers() {
        // 可以访问:
        grandParentProtected = 10;  // ✓ 通过protected继承链传递
        grandParentPublic = 20;     // ✓ 虽然在Parent中是protected
        parentProtected = 30;       // ✓ 
        parentPublic = 40;          // ✓
    }
};
 
class GrandChild : public Child {
public:
    void accessMembers() {
        // 继续可以访问所有protected和public成员
        grandParentProtected = 100;
        grandParentPublic = 200;
        parentProtected = 300;
        parentPublic = 400;
    }
};

三、友元机制

3.1 友元函数

class FriendDemo {
private:
    int privateData;
    void privateMethod() { std::cout << "Private method\n"; }
    
protected:
    int protectedData;
    
public:
    FriendDemo() : privateData(10), protectedData(20) {}
    
    // 声明友元函数
    friend void friendFunction(FriendDemo& obj);
    friend void anotherFriend(const FriendDemo& obj);
    
    // 友元运算符重载
    friend std::ostream& operator<<(std::ostream& os, const FriendDemo& obj);
    friend FriendDemo operator+(const FriendDemo& a, const FriendDemo& b);
};
 
// 友元函数实现
void friendFunction(FriendDemo& obj) {
    obj.privateData = 100;      // ✓ 可以访问private
    obj.protectedData = 200;    // ✓ 可以访问protected
    obj.privateMethod();         // ✓ 可以调用private方法
}
 
std::ostream& operator<<(std::ostream& os, const FriendDemo& obj) {
    os << "Private: " << obj.privateData 
       << ", Protected: " << obj.protectedData;
    return os;
}

3.2 友元类

class FriendClass {
private:
    int secret = 42;
    
    // 声明友元类
    friend class TrustedFriend;
    
    // 只声明特定成员函数为友元
    friend void PartialFriend::specificMethod(FriendClass&);
    
    // 模板友元
    template<typename T>
    friend class TemplateFriend;
};
 
class TrustedFriend {
public:
    void accessFriend(FriendClass& fc) {
        std::cout << "Accessing secret: " << fc.secret << "\n";
        fc.secret = 100;  // ✓ 可以修改private成员
    }
    
    static void staticAccess(FriendClass& fc) {
        fc.secret = 200;  // ✓ 静态函数也可以访问
    }
};
 
class PartialFriend {
public:
    void specificMethod(FriendClass& fc) {
        fc.secret = 300;  // ✓ 只有这个函数是友元
    }
    
    void otherMethod(FriendClass& fc) {
        // fc.secret = 400;  // ✗ 这个函数不是友元
    }
};

3.3 友元的传递性和继承

class A {
    friend class B;
private:
    int dataA = 10;
};
 
class B {
    friend class C;  // B的友元
private:
    int dataB = 20;
public:
    void accessA(A& a) {
        a.dataA = 100;  // ✓ B是A的友元
    }
};
 
class C {
public:
    void tryAccess(A& a, B& b) {
        // a.dataA = 200;  // ✗ 友元关系不传递
        b.dataB = 200;     // ✓ C是B的友元
    }
};
 
// 友元关系不能继承
class DerivedB : public B {
public:
    void tryAccessA(A& a) {
        // a.dataA = 300;  // ✗ 派生类不继承友元关系
    }
};

四、访问控制的设计模式

4.1 封装模式

class BankAccount {
private:
    double balance;
    std::string accountNumber;
    std::vector<std::string> transactionHistory;
    
    // 私有辅助方法
    void logTransaction(const std::string& transaction) {
        transactionHistory.push_back(transaction);
    }
    
    bool validateAmount(double amount) const {
        return amount > 0 && amount <= balance;
    }
    
public:
    BankAccount(const std::string& accNum, double initialBalance)
        : accountNumber(accNum), balance(initialBalance) {
        logTransaction("Account created with balance: " + 
                      std::to_string(initialBalance));
    }
    
    // 公有接口提供受控访问
    bool withdraw(double amount) {
        if (validateAmount(amount)) {
            balance -= amount;
            logTransaction("Withdrawn: " + std::to_string(amount));
            return true;
        }
        return false;
    }
    
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            logTransaction("Deposited: " + std::to_string(amount));
        }
    }
    
    // 只读访问
    double getBalance() const { return balance; }
    
    // 提供历史记录的只读访问
    std::vector<std::string> getHistory() const {
        return transactionHistory;  // 返回副本
    }
};

4.2 Pimpl惯用法(编译防火墙)

// Widget.h - 公开接口
class Widget {
    class Impl;  // 前向声明
    std::unique_ptr<Impl> pImpl;
    
public:
    Widget();
    ~Widget();  // 必须在cpp文件中定义
    
    Widget(const Widget&);
    Widget& operator=(const Widget&);
    Widget(Widget&&) noexcept;
    Widget& operator=(Widget&&) noexcept;
    
    // 公有接口
    void doSomething();
    int getValue() const;
};
 
// Widget.cpp - 实现细节
class Widget::Impl {
private:
    // 所有私有数据都在这里
    int value;
    std::string name;
    std::vector<int> data;
    
public:
    Impl() : value(0), name("default") {}
    
    void doSomething() {
        // 实现细节完全隐藏
        value++;
    }
    
    int getValue() const { return value; }
};
 
Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
 
void Widget::doSomething() {
    pImpl->doSomething();
}
 
int Widget::getValue() const {
    return pImpl->getValue();
}

4.3 Attorney-Client 惯用法

class Client {
private:
    void privateMethod1() { std::cout << "Private 1\n"; }
    void privateMethod2() { std::cout << "Private 2\n"; }
    void privateMethod3() { std::cout << "Private 3\n"; }
    
    // Attorney类提供选择性访问
    friend class Attorney;
};
 
// Attorney类控制访问权限
class Attorney {
private:
    friend class User1;
    friend class User2;
    
    // 为User1提供的接口
    static void callPrivateMethod1(Client& c) {
        c.privateMethod1();
    }
    
    // 为User2提供的接口
    static void callPrivateMethod2(Client& c) {
        c.privateMethod2();
    }
    
    // privateMethod3不暴露给任何人
};
 
class User1 {
public:
    void useClient(Client& c) {
        Attorney::callPrivateMethod1(c);  // ✓ 只能访问Method1
        // Attorney::callPrivateMethod2(c); // ✗ 不能访问Method2
    }
};
 
class User2 {
public:
    void useClient(Client& c) {
        // Attorney::callPrivateMethod1(c); // ✗ 不能访问Method1
        Attorney::callPrivateMethod2(c);     // ✓ 只能访问Method2
    }
};

五、嵌套类的访问控制

5.1 嵌套类访问规则

class Outer {
private:
    int outerPrivate = 10;
    static int outerStaticPrivate;
    
protected:
    int outerProtected = 20;
    
public:
    int outerPublic = 30;
    
    // 公有嵌套类
    class PublicNested {
    private:
        int nestedPrivate = 100;
        
    public:
        void accessOuter(Outer& outer) {
            // 嵌套类可以访问外部类的所有成员
            outer.outerPrivate = 11;     // ✓
            outer.outerProtected = 21;   // ✓
            outer.outerPublic = 31;      // ✓
            outerStaticPrivate = 41;     // ✓ 访问静态私有成员
        }
        
        friend void Outer::outerMethod();  // 外部类成员函数作为友元
    };
    
private:
    // 私有嵌套类
    class PrivateNested {
    public:
        void method() {
            Outer outer;
            outer.outerPrivate = 12;     // ✓ 可以访问外部类私有成员
        }
    };
    
protected:
    // 保护嵌套类
    class ProtectedNested {
    public:
        int data = 200;
    };
    
public:
    void outerMethod() {
        PublicNested pn;
        pn.nestedPrivate = 101;  // ✓ 如果声明为友元
        
        PrivateNested prn;
        prn.method();             // ✓ 外部类可以使用私有嵌套类
        
        ProtectedNested pon;
        pon.data = 201;          // ✓
    }
};
 
int Outer::outerStaticPrivate = 40;
 
// 外部使用
void externalUse() {
    Outer outer;
    Outer::PublicNested nested;  // ✓ 可以使用公有嵌套类
    // Outer::PrivateNested pn;  // ✗ 不能使用私有嵌套类
    // Outer::ProtectedNested;    // ✗ 不能使用保护嵌套类
}

六、访问控制的最佳实践

6.1 最小权限原则

class MinimalAccess {
private:
    // 默认所有成员都应该是private
    int internalData;
    std::vector<int> cache;
    
    // 内部辅助函数
    void updateCache() {
        // 内部实现
    }
    
    bool validate(int value) const {
        return value >= 0 && value <= 100;
    }
    
protected:
    // 只有当确实需要被派生类访问时才用protected
    virtual void customizableOperation() {
        // 派生类可以重写
    }
    
    int getInternalData() const { return internalData; }
    
public:
    // 只暴露必要的接口
    void publicOperation(int value) {
        if (validate(value)) {
            internalData = value;
            updateCache();
            customizableOperation();
        }
    }
    
    // const正确性
    int getValue() const { return internalData; }
};

6.2 接口与实现分离

// 接口类(抽象基类)
class IDataProcessor {
public:
    virtual ~IDataProcessor() = default;
    virtual void processData(const std::vector<int>& data) = 0;
    virtual int getResult() const = 0;
};
 
// 实现类
class DataProcessor : public IDataProcessor {
private:
    // 所有实现细节都是private
    int result;
    std::vector<int> intermediateData;
    
    void step1(const std::vector<int>& data) {
        // 私有实现
    }
    
    void step2() {
        // 私有实现
    }
    
    void step3() {
        // 私有实现
    }
    
public:
    // 只公开接口方法
    void processData(const std::vector<int>& data) override {
        step1(data);
        step2();
        step3();
    }
    
    int getResult() const override {
        return result;
    }
};

6.3 getter/setter 设计

class PropertyDesign {
private:
    int value;
    mutable int cachedComputation;
    mutable bool cacheValid;
    
public:
    // 简单getter
    int getValue() const { return value; }
    
    // 带验证的setter
    bool setValue(int newValue) {
        if (newValue >= 0 && newValue <= 100) {
            value = newValue;
            cacheValid = false;
            return true;
        }
        return false;
    }
    
    // 计算属性(延迟计算)
    int getComputedValue() const {
        if (!cacheValid) {
            cachedComputation = value * value + 2 * value;
            cacheValid = true;
        }
        return cachedComputation;
    }
    
    // 属性类模拟
    class Property {
    private:
        int& ref;
        std::function<bool(int)> validator;
        
    public:
        Property(int& r, std::function<bool(int)> v) 
            : ref(r), validator(v) {}
        
        operator int() const { return ref; }
        
        Property& operator=(int value) {
            if (validator(value)) {
                ref = value;
            }
            return *this;
        }
    };
};

七、访问控制与模板

7.1 模板中的访问控制

template<typename T>
class TemplateAccess {
private:
    T privateData;
    
protected:
    T protectedData;
    
public:
    T publicData;
    
    // 模板友元
    template<typename U>
    friend class TemplateAccess;  // 所有实例化都是友元
    
    // 特定实例化为友元
    friend class TemplateAccess<int>;
    
    // 模板成员函数
    template<typename U>
    void convert(const TemplateAccess<U>& other) {
        // 可以访问其他实例化的私有成员(如果声明为友元)
        privateData = static_cast<T>(other.privateData);
    }
};
 
// 显式实例化的访问控制
template class TemplateAccess<int>;  // 显式实例化
 
// 模板特化的访问控制
template<>
class TemplateAccess<std::string> {
private:
    std::string specialPrivate;
    
public:
    void specialMethod() {
        // 特化版本的特殊实现
    }
};

八、现代C++的访问控制特性

8.1 C++11/14/17新特性

class ModernFeatures {
private:
    int data = 42;  // C++11 类内初始化
    
public:
    // = default 和 = delete
    ModernFeatures() = default;
    ModernFeatures(const ModernFeatures&) = delete;  // 禁止拷贝
    
    // final关键字
    virtual void method() final {  // 不能被重写
        // 实现
    }
    
    // override确保正确重写
    class Derived : public ModernFeatures {
        // void method() override {}  // 错误:final方法不能重写
    };
    
    // constexpr
    constexpr int getConstValue() const { return 100; }
    
    // [[nodiscard]] 属性
    [[nodiscard]] int importantValue() const { return data; }
};
 
// C++17 结构化绑定与访问控制
class StructuredBinding {
public:
    int x = 1;
    int y = 2;
    
private:
    int z = 3;  // 不参与结构化绑定
    
public:
    // 提供结构化绑定支持
    auto operator<=>(const StructuredBinding&) const = default;
};

8.2 C++20 模块中的访问控制

// module.ixx
export module MyModule;
 
export class ExportedClass {
private:
    int privateData;
    
public:
    void publicMethod();
};
 
// 不导出的类
class InternalClass {
    // 模块内部使用
};
 
// 导出特定成员
export {
    void someFunction();
    class AnotherClass;
}

九、访问控制的常见陷阱与解决方案

9.1 常见错误

class CommonMistakes {
    // 错误1:过度使用public
    class BadDesign {
    public:
        int x, y, z;  // 破坏封装
        void internal_helper();  // 内部函数不应public
    };
    
    // 正确做法
    class GoodDesign {
    private:
        int x, y, z;
        void internal_helper();
        
    public:
        void setPosition(int nx, int ny, int nz);
        std::tuple<int, int, int> getPosition() const;
    };
    
    // 错误2:友元滥用
    class FriendAbuse {
        friend class Everyone;  // 过度信任
        friend void globalFunction();  // 破坏封装
    };
    
    // 错误3:protected数据成员
    class BaseWithProtectedData {
    protected:
        int data;  // 派生类可直接修改,破坏不变量
    };
    
    // 正确做法
    class BaseWithProtectedInterface {
    private:
        int data;
        
    protected:
        int getData() const { return data; }
        void setData(int d) { 
            // 可以添加验证
            data = d; 
        }
    };
};

访问控制是C++面向对象编程的核心特性之一,正确使用能够实现良好的封装,提高代码的可维护性和安全性。关键是要遵循最小权限原则,只暴露必要的接口,隐藏实现细节。