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