在C++面向对象编程中,访问控制是实现封装特性的核心机制。private、protected和public这三个关键字构成了类成员访问权限的完整体系,它们决定了类成员在不同上下文中的可见性范围。
访问控制符从根本上解决了两个问题:
从编译器实现角度看,访问控制是在编译阶段进行的静态检查。当代码违反访问规则时,编译器会直接报错,这种检查不会带来运行时开销。
注意:访问权限仅作用于代码层面的访问控制,通过指针强制类型转换等技巧仍可能绕过限制,但这会破坏封装性并带来安全隐患。
private成员是封装性最强的访问级别,具有以下特点:
典型应用场景:
cpp复制class BankAccount {
private:
double balance; // 账户余额需要严格保护
string password; // 敏感信息
public:
void withdraw(double amount) {
if(amount <= balance) balance -= amount;
}
};
protected权限设计用于继承体系,特点包括:
实际开发中的典型用法:
cpp复制class Shape {
protected:
int vertexCount; // 派生类需要使用的属性
public:
virtual double area() = 0;
};
class Triangle : public Shape {
public:
double area() override {
// 可以访问基类的protected成员
if(vertexCount != 3) throw logic_error("Invalid shape");
// 计算面积...
}
};
public成员构成类的对外接口:
接口设计示例:
cpp复制class NetworkConnection {
public:
enum Status { DISCONNECTED, CONNECTING, CONNECTED };
Status getStatus() const { return status_; }
void connect(const string& url);
void disconnect();
private:
Status status_;
// 内部实现细节...
};
继承时指定的访问修饰符会影响基类成员在派生类中的可见性:
| 基类成员权限 | public继承 | protected继承 | private继承 |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | 不可访问 | 不可访问 | 不可访问 |
cpp复制class Derived : public Base {
// 保持基类接口不变
};
cpp复制class UtilityImpl : protected Utility {
// 将基类接口转为protected
};
cpp复制class Stack : private LinkedList {
// 将链表实现细节完全隐藏
};
重要原则:除非特别需要,否则应该优先使用public继承。protected/private继承通常表明设计可能存在问题。
友元打破了封装边界,应谨慎使用:
cpp复制class SecureContainer {
private:
string secretData;
// 授予特定函数访问权限
friend void securityAudit(SecureContainer&);
};
// 友元函数实现
void securityAudit(SecureContainer& container) {
cout << container.secretData; // 允许访问private成员
}
通过成员函数实现运行时访问控制:
cpp复制class ConfigManager {
public:
string getConfig(const string& key) const {
if(!checkAccess()) throw PermissionDenied();
return configs_.at(key);
}
private:
map<string, string> configs_;
bool checkAccess() const;
};
cpp复制class Singleton {
public:
static Singleton& instance() {
static Singleton inst;
return inst;
}
// 禁用拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default; // 构造私有化
};
cpp复制class Product {
protected:
Product() = default; // 只允许派生类构造
public:
virtual ~Product() = default;
};
class ConcreteProduct : public Product {
public:
ConcreteProduct() = default;
};
class Factory {
public:
static Product* create() {
return new ConcreteProduct();
}
};
良好的类设计应该遵循访问权限逐步放宽的原则:
在不同平台上,访问控制可能有一些特殊表现:
典型错误案例:
cpp复制class MyClass {
public:
int value1; // 不应该public数据成员
protected:
void helper(); // 实际不需要子类使用
private:
virtual void internal(); // private虚函数通常无意义
};
修正建议:
cpp复制class MyClass {
private:
int value1_; // 数据私有化
public:
int value1() const { return value1_; } // 提供访问接口
protected:
// 明确标注需要子类重写的方法
virtual void overridableHelper();
};
常见错误:
cpp复制class Base {
protected:
void importantMethod();
};
class Derived : private Base {
public:
using Base::importantMethod; // 尝试恢复访问权限
};
解决方案:
cpp复制class Derived : private Base {
public:
// 提供包装接口
void importantMethodWrapper() {
Base::importantMethod();
}
};
cpp复制class Base {
public:
virtual void api() final; // 禁止重写
};
class Derived : public Base {
public:
void api() override; // 编译错误
};
cpp复制class Shape {
protected:
virtual void drawInternal() = 0;
};
class Circle : public Shape {
protected:
void drawInternal() override; // 明确表示重写
};
C++20模块系统中,访问控制有了新的维度:
cpp复制export module MyModule;
class Visible {
public:
void interface(); // 模块外可见
private:
void implementation(); // 仅模块内可见
};
访问权限本身不会带来运行时开销,但会影响编译器优化:
cpp复制class MyClass {
private:
int internalState_;
friend class MyClassTest;
};
class MyClassTest {
public:
static int getState(const MyClass& obj) {
return obj.internalState_;
}
};
cpp复制#ifdef UNIT_TESTING
public:
int testGetInternalState() const { return state_; }
#endif
cpp复制template<typename T>
class TestAccessor;
template<>
class TestAccessor<MyClass> {
public:
static int getState(const MyClass& obj) {
return obj.state_;
}
};
在模块化设计中,访问控制需要分层处理:
良好的文档应该说明:
cpp复制class Processor {
public:
/// @public_api 稳定接口,保证向后兼容
void process();
protected:
/// @extension_point 子类可重写
virtual void preProcess();
private:
/// @implementation_detail 内部使用,可能随时修改
void internalHelper();
};
当需要提供C接口时:
cpp复制extern "C" {
struct CMyClass; // 不透明指针
void cmethod(CMyClass* obj);
}
class MyClass {
private:
// 实际实现...
public:
static CMyClass* create() { return reinterpret_cast<CMyClass*>(new MyClass); }
};
模板可以突破常规访问限制:
cpp复制template<typename T>
struct AccessPrivate {
static auto getPrivate(T& t) {
return t.privateMember; // 如果特化中privateMember可访问
}
};
编译期计算可以绕过某些限制:
cpp复制class ConstExprDemo {
private:
static constexpr int secret = 42;
public:
constexpr static int getSecret() { return secret; }
};
cpp复制class SecureData {
private:
char* password_;
public:
~SecureData() {
if(password_) {
memset(password_, 0, strlen(password_));
delete[] password_;
}
}
};
public接口应该验证输入:
cpp复制class ConfigLoader {
public:
void load(const string& filename) {
if(!isValidPath(filename)) throw SecurityException();
// 继续加载...
}
private:
bool isValidPath(const string& path);
};
保持二进制兼容性的技巧:
现代工具可以检测:
常用命名风格:
cpp复制class Example {
private:
int m_count; // m_前缀
string name_; // _后缀
protected:
double internalTemp; // 无特殊标记
public:
void publicMethod(); // 首字母小写
};
传统实现:
cpp复制class Subject {
private:
vector<Observer*> observers_; // 私有维护观察者列表
protected:
void notify() { /* 通知观察者 */ }
public:
void attach(Observer* o) { observers_.push_back(o); }
};
现代C++变体:
cpp复制class Subject {
public:
using Observer = function<void()>;
private:
vector<Observer> observers_; // 使用function包装
};
通过private继承实现:
cpp复制class SortingPolicy {
public:
virtual void sort(vector<int>&) = 0;
};
class Sorter : private SortingPolicy {
public:
void sortData(vector<int>& data) {
SortingPolicy::sort(data); // 委托给策略
}
};
cpp复制class ThreadSafeQueue {
public:
void push(int value) {
lock_guard<mutex> lock(mutex_);
data_.push_back(value);
}
private:
mutex mutex_;
vector<int> data_;
};
对高频访问的private成员:
cpp复制class Counter {
private:
atomic<int> count_{0};
public:
int increment() { return ++count_; }
};