Menu
CoddyTech

深入了解访问控制符

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 35 课,共 104 课。

现在你已经了解了三种访问说明符,接下来我们来看看同一个类有多个对象时,它们的行为有什么不同。一个常见的误解是,private 表示“只有这个对象”——但它实际上表示“只有这个类”。

同一个类的对象可以访问彼此的私有成员:

class Person {
    std::string secret;
    
public:
    Person(std::string s) : secret(s) {}
    
    void readSecret(const Person& other) {
        // 可以访问另一个对象的私有成员!
        std::cout << other.secret;
    }
};

这是因为访问控制是在类级别实施的,而不是对象级别。readSecret 方法属于 Person 类,因此可以访问任何 Person 对象的私有成员。这种设计使复制构造函数和比较运算符等操作能够使用其他对象的内部状态。

你还可以在一个类中多次切换访问修饰符:

class Mixed {
public:
    void publicMethod1();
    
private:
    int privateData;
    
public:
    void publicMethod2();    // 重新设为 public
    
private:
    void privateHelper();    // 再次设为 private
};

虽然这段代码在 C++ 中有效,但为了提高可读性,通常最好按访问级别对成员进行分组。大多数风格指南建议先列出 public 成员(因为它们构成类的接口),然后是 protected,最后是 private。

challenge icon

挑战

简单

我们来构建一个钱包比较系统,演示同一个类的对象如何访问彼此的私有成员。这是理解 C++ 访问控制的一个关键点:访问控制是在类级别执行的,而不是在对象级别。

你将创建两个文件来组织代码:

  • Wallet.h:定义一个 Wallet 类,用于存储私有财务数据,同时允许钱包之间进行比较。你的类应包含:
    • 私有成员:ownerName(字符串)和 balance(double)
    • 一个接收所有者姓名和初始余额的构造函数
    • 一个公有 getter getOwnerName(),用于返回所有者的姓名
    • 一个方法 hasMoreThan(const Wallet& other),用于将当前钱包的余额与另一个钱包的私有余额进行比较。如果当前钱包的钱更多,则返回 true
    • 一个方法 combinedBalance(const Wallet& other),用于返回两个钱包的私有余额之和
    • 一个方法 transferTo(Wallet& other, double amount),用于将钱从当前钱包转到另一个钱包。它应直接修改两个钱包的私有余额(仅当金额为正数且不超过当前钱包的余额时)
  • main.cpp:演示跨对象访问私有成员。从输入中读取两个所有者姓名和两个余额(共 4 行:name1、balance1、name2、balance2),然后:
    • 使用输入的值创建两个 Wallet 对象
    • 使用 combinedBalance() 打印 "Combined wealth: $<amount>"
    • 比较两个钱包,并为余额较高的所有者打印 "<name> has more money"(使用 hasMoreThan() 来判断)
    • 使用 transferTo() 将 100.0 从第一个钱包转到第二个钱包
    • 打印 "After transfer:"
    • 再次打印 "Combined wealth: $<amount>"(总额应保持不变)
    • 再次比较,并打印 "<name> has more money"(结果可能会改变)

这里的关键概念是,像 hasMoreThan() 和 transferTo() 这样的方法可以直接访问另一个 Wallet 对象的私有成员 balance:不需要 getter。这是因为 C++ 中的访问控制是基于类的:Wallet 类中的任何代码都可以访问任意 Wallet 对象的私有成员。

使用 std::fixed 和 std::setprecision(2)(来自 <iomanip>)将所有金额格式化为两位小数。使用 std::stod() 转换输入的余额。

亲手试试

#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"

using namespace std;

int main() {
    // 读取输入:name1、balance1、name2、balance2
    string name1, name2;
    string balanceStr1, balanceStr2;
    
    getline(cin, name1);
    getline(cin, balanceStr1);
    getline(cin, name2);
    getline(cin, balanceStr2);
    
    double balance1 = stod(balanceStr1);
    double balance2 = stod(balanceStr2);
    
    // 设置金额的输出格式
    cout << fixed << setprecision(2);
    
    // TODO: 用输入值创建两个 Wallet 对象
    
    // TODO: 使用 combinedBalance() 输出总资产
    // 格式:"Combined wealth: $<amount>"
    
    // TODO: 使用 hasMoreThan() 比较两个钱包,并输出谁的钱更多
    // 格式:"<name> has more money"
    
    // TODO: 使用 transferTo() 从第一个钱包向第二个钱包转账 100.0
    
    // TODO: 输出 "After transfer:"
    
    // TODO: 再次输出总资产
    
    // TODO: 再次比较并输出谁的钱更多
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器