Skip to content

C++ 封装

概述

封装(Encapsulation)是面向对象编程的基本原则之一,它将数据(属性)和操作数据的方法(行为)绑定在一起,并隐藏对象的内部实现细节。封装通过访问控制机制保护数据的完整性,提供清晰的对外接口。

🔒 基本封装概念

数据隐藏和访问控制

cpp
#include <iostream>
#include <string>
#include <stdexcept>

class BankAccount {
private:
    // 私有数据成员:外部无法直接访问
    std::string accountNumber_;
    std::string ownerName_;
    double balance_;
    std::string pin_;
    bool locked_;
    
    // 私有辅助方法
    bool validatePin(const std::string& inputPin) const {
        return pin_ == inputPin;
    }
    
    void logTransaction(const std::string& operation, double amount) const {
        std::cout << "[日志] " << accountNumber_ << ": " << operation 
                  << " " << amount << ", 余额: " << balance_ << std::endl;
    }
    
public:
    // 构造函数:确保对象创建时的有效状态
    BankAccount(const std::string& accountNumber, const std::string& ownerName, 
                const std::string& pin, double initialBalance = 0.0)
        : accountNumber_(accountNumber), ownerName_(ownerName), 
          pin_(pin), balance_(initialBalance), locked_(false) {
        
        if (initialBalance < 0) {
            throw std::invalid_argument("初始余额不能为负数");
        }
        
        std::cout << "账户创建成功: " << accountNumber_ << std::endl;
    }
    
    // 公共接口:控制对数据的访问
    bool deposit(double amount, const std::string& pin) {
        if (!validatePin(pin)) {
            std::cout << "PIN码错误" << std::endl;
            return false;
        }
        
        if (locked_) {
            std::cout << "账户已锁定" << std::endl;
            return false;
        }
        
        if (amount <= 0) {
            std::cout << "存款金额必须大于0" << std::endl;
            return false;
        }
        
        balance_ += amount;
        logTransaction("存款", amount);
        return true;
    }
    
    bool withdraw(double amount, const std::string& pin) {
        if (!validatePin(pin)) {
            std::cout << "PIN码错误" << std::endl;
            return false;
        }
        
        if (locked_) {
            std::cout << "账户已锁定" << std::endl;
            return false;
        }
        
        if (amount <= 0) {
            std::cout << "取款金额必须大于0" << std::endl;
            return false;
        }
        
        if (amount > balance_) {
            std::cout << "余额不足" << std::endl;
            return false;
        }
        
        balance_ -= amount;
        logTransaction("取款", amount);
        return true;
    }
    
    // 只读访问器
    double getBalance(const std::string& pin) const {
        if (!validatePin(pin)) {
            std::cout << "PIN码错误" << std::endl;
            return -1;  // 错误标识
        }
        
        return balance_;
    }
    
    std::string getAccountNumber() const {
        return accountNumber_;  // 账号可以公开
    }
    
    std::string getOwnerName() const {
        return ownerName_;
    }
    
    // 安全操作
    void lockAccount() {
        locked_ = true;
        std::cout << "账户已锁定" << std::endl;
    }
    
    void unlockAccount(const std::string& pin) {
        if (validatePin(pin)) {
            locked_ = false;
            std::cout << "账户已解锁" << std::endl;
        } else {
            std::cout << "PIN码错误,无法解锁" << std::endl;
        }
    }
    
    bool isLocked() const {
        return locked_;
    }
};

int main() {
    std::cout << "=== 封装基础 ===" << std::endl;
    
    try {
        BankAccount account("123456789", "张三", "1234", 1000.0);
        
        // 通过公共接口安全操作
        std::cout << "账户所有者: " << account.getOwnerName() << std::endl;
        std::cout << "余额: " << account.getBalance("1234") << std::endl;
        
        // 正常操作
        account.deposit(500, "1234");
        account.withdraw(200, "1234");
        
        // 错误操作被阻止
        account.withdraw(2000, "1234");  // 余额不足
        account.deposit(100, "5678");    // PIN错误
        
        // 安全机制
        account.lockAccount();
        account.deposit(100, "1234");    // 账户锁定
        account.unlockAccount("1234");
        
        // 无法直接访问私有成员
        // std::cout << account.balance_;     // 编译错误
        // account.pin_ = "0000";             // 编译错误
        
    } catch (const std::exception& e) {
        std::cout << "错误: " << e.what() << std::endl;
    }
    
    return 0;
}

🎯 Getter和Setter方法

访问器和修改器设计

cpp
#include <iostream>
#include <string>
#include <algorithm>

class Employee {
private:
    std::string name_;
    int age_;
    double salary_;
    std::string department_;
    int employeeId_;
    static int nextId_;
    
public:
    Employee(const std::string& name, int age, double salary, const std::string& department)
        : name_(name), age_(age), salary_(salary), department_(department), employeeId_(++nextId_) {
        validateEmployee();
    }
    
    // Getter方法:提供只读访问
    std::string getName() const { return name_; }
    int getAge() const { return age_; }
    double getSalary() const { return salary_; }
    std::string getDepartment() const { return department_; }
    int getEmployeeId() const { return employeeId_; }
    
    // Setter方法:提供受控修改
    void setName(const std::string& name) {
        if (name.empty()) {
            throw std::invalid_argument("姓名不能为空");
        }
        name_ = name;
        std::cout << "姓名已更新为: " << name_ << std::endl;
    }
    
    void setAge(int age) {
        if (age < 18 || age > 65) {
            throw std::invalid_argument("年龄必须在18-65之间");
        }
        age_ = age;
        std::cout << "年龄已更新为: " << age_ << std::endl;
    }
    
    void setSalary(double salary) {
        if (salary < 0) {
            throw std::invalid_argument("薪资不能为负数");
        }
        
        double oldSalary = salary_;
        salary_ = salary;
        
        std::cout << "薪资已从 " << oldSalary << " 更新为 " << salary_ << std::endl;
        
        // 记录薪资变动
        if (salary > oldSalary) {
            std::cout << "恭喜!薪资上涨了 " << (salary - oldSalary) << std::endl;
        }
    }
    
    void setDepartment(const std::string& department) {
        if (department.empty()) {
            throw std::invalid_argument("部门不能为空");
        }
        
        std::string oldDept = department_;
        department_ = department;
        std::cout << "部门已从 " << oldDept << " 转移到 " << department_ << std::endl;
    }
    
    // 只读属性:员工ID不允许修改
    // 不提供setEmployeeId方法
    
    // 计算属性
    double getAnnualSalary() const {
        return salary_ * 12;
    }
    
    std::string getDisplayName() const {
        return name_ + " (#" + std::to_string(employeeId_) + ")";
    }
    
    void displayInfo() const {
        std::cout << "员工信息:" << std::endl;
        std::cout << "  ID: " << employeeId_ << std::endl;
        std::cout << "  姓名: " << name_ << std::endl;
        std::cout << "  年龄: " << age_ << std::endl;
        std::cout << "  部门: " << department_ << std::endl;
        std::cout << "  月薪: " << salary_ << std::endl;
        std::cout << "  年薪: " << getAnnualSalary() << std::endl;
    }
    
private:
    void validateEmployee() {
        if (name_.empty()) {
            throw std::invalid_argument("姓名不能为空");
        }
        if (age_ < 18 || age_ > 65) {
            throw std::invalid_argument("年龄必须在18-65之间");
        }
        if (salary_ < 0) {
            throw std::invalid_argument("薪资不能为负数");
        }
        if (department_.empty()) {
            throw std::invalid_argument("部门不能为空");
        }
    }
};

int Employee::nextId_ = 0;

int main() {
    std::cout << "=== Getter/Setter示例 ===" << std::endl;
    
    try {
        Employee emp("李四", 28, 8000, "技术部");
        emp.displayInfo();
        
        std::cout << "\n--- 更新员工信息 ---" << std::endl;
        emp.setAge(30);
        emp.setSalary(10000);
        emp.setDepartment("产品部");
        
        std::cout << "\n--- 计算属性 ---" << std::endl;
        std::cout << "显示名称: " << emp.getDisplayName() << std::endl;
        std::cout << "年薪: " << emp.getAnnualSalary() << std::endl;
        
        // 尝试无效操作
        std::cout << "\n--- 验证错误处理 ---" << std::endl;
        try {
            emp.setAge(70);  // 年龄超出范围
        } catch (const std::exception& e) {
            std::cout << "捕获异常: " << e.what() << std::endl;
        }
        
        try {
            emp.setSalary(-1000);  // 负薪资
        } catch (const std::exception& e) {
            std::cout << "捕获异常: " << e.what() << std::endl;
        }
        
    } catch (const std::exception& e) {
        std::cout << "创建员工失败: " << e.what() << std::endl;
    }
    
    return 0;
}

🏛️ 类的内聚性设计

高内聚的类设计

cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// 高内聚:所有成员都围绕"学生成绩"这一核心概念
class StudentGrades {
private:
    std::string studentName_;
    std::string studentId_;
    std::vector<double> grades_;
    
    // 私有辅助方法:支持核心功能
    bool isValidGrade(double grade) const {
        return grade >= 0.0 && grade <= 100.0;
    }
    
    void sortGrades() {
        std::sort(grades_.begin(), grades_.end(), std::greater<double>());
    }
    
public:
    StudentGrades(const std::string& name, const std::string& id) 
        : studentName_(name), studentId_(id) {}
    
    // 核心功能:成绩管理
    bool addGrade(double grade) {
        if (!isValidGrade(grade)) {
            std::cout << "无效成绩: " << grade << " (必须在0-100之间)" << std::endl;
            return false;
        }
        
        grades_.push_back(grade);
        std::cout << "添加成绩: " << grade << std::endl;
        return true;
    }
    
    bool removeLowestGrade() {
        if (grades_.empty()) {
            std::cout << "没有成绩可删除" << std::endl;
            return false;
        }
        
        auto min_it = std::min_element(grades_.begin(), grades_.end());
        double removed = *min_it;
        grades_.erase(min_it);
        std::cout << "删除最低成绩: " << removed << std::endl;
        return true;
    }
    
    // 核心计算功能
    double getAverageGrade() const {
        if (grades_.empty()) return 0.0;
        
        double sum = 0.0;
        for (double grade : grades_) {
            sum += grade;
        }
        return sum / grades_.size();
    }
    
    double getHighestGrade() const {
        if (grades_.empty()) return 0.0;
        return *std::max_element(grades_.begin(), grades_.end());
    }
    
    double getLowestGrade() const {
        if (grades_.empty()) return 0.0;
        return *std::min_element(grades_.begin(), grades_.end());
    }
    
    char getLetterGrade() const {
        double avg = getAverageGrade();
        if (avg >= 90) return 'A';
        if (avg >= 80) return 'B';
        if (avg >= 70) return 'C';
        if (avg >= 60) return 'D';
        return 'F';
    }
    
    // 访问器
    std::string getStudentName() const { return studentName_; }
    std::string getStudentId() const { return studentId_; }
    size_t getGradeCount() const { return grades_.size(); }
    
    // 显示功能
    void displayGrades() const {
        std::cout << "\n学生: " << studentName_ << " (ID: " << studentId_ << ")" << std::endl;
        std::cout << "成绩列表: ";
        
        if (grades_.empty()) {
            std::cout << "无成绩" << std::endl;
            return;
        }
        
        for (size_t i = 0; i < grades_.size(); ++i) {
            std::cout << grades_[i];
            if (i < grades_.size() - 1) std::cout << ", ";
        }
        
        std::cout << std::endl;
        std::cout << "平均分: " << getAverageGrade() << std::endl;
        std::cout << "最高分: " << getHighestGrade() << std::endl;
        std::cout << "最低分: " << getLowestGrade() << std::endl;
        std::cout << "等级: " << getLetterGrade() << std::endl;
    }
    
    void displaySortedGrades() const {
        std::cout << "\n排序后的成绩 (从高到低): ";
        auto sorted_grades = grades_;
        std::sort(sorted_grades.begin(), sorted_grades.end(), std::greater<double>());
        
        for (size_t i = 0; i < sorted_grades.size(); ++i) {
            std::cout << sorted_grades[i];
            if (i < sorted_grades.size() - 1) std::cout << " > ";
        }
        std::cout << std::endl;
    }
};

// 对比:低内聚的设计(不推荐)
class BadStudent {
public:
    std::string name;
    std::vector<double> grades;
    std::string address;          // 与成绩无关
    std::string phoneNumber;      // 与成绩无关
    
    void addGrade(double grade) { grades.push_back(grade); }
    void sendEmail() {}           // 与学生成绩管理无关
    void calculateTax() {}        // 完全无关的功能
    void manageLibraryBooks() {}  // 职责混乱
};

int main() {
    std::cout << "=== 类的内聚性设计 ===" << std::endl;
    
    StudentGrades student("王小明", "2023001");
    
    // 添加成绩
    student.addGrade(85.5);
    student.addGrade(92.0);
    student.addGrade(78.5);
    student.addGrade(88.0);
    
    // 尝试添加无效成绩
    student.addGrade(105.0);  // 超出范围
    student.addGrade(-10.0);  // 负数
    
    // 显示成绩信息
    student.displayGrades();
    student.displaySortedGrades();
    
    // 删除最低成绩
    std::cout << "\n--- 删除最低成绩 ---" << std::endl;
    student.removeLowestGrade();
    student.displayGrades();
    
    std::cout << "\n=== 设计原则 ===" << std::endl;
    std::cout << "✓ 高内聚:所有成员都围绕核心职责" << std::endl;
    std::cout << "✓ 单一职责:只负责学生成绩管理" << std::endl;
    std::cout << "✓ 数据验证:确保数据完整性" << std::endl;
    std::cout << "✓ 接口清晰:提供明确的操作方法" << std::endl;
    
    return 0;
}

🛡️ 不变式和数据完整性

类不变式维护

cpp
#include <iostream>
#include <vector>
#include <stdexcept>
#include <algorithm>

class Rectangle {
private:
    double width_;
    double height_;
    
    // 类不变式:宽度和高度必须为正数
    void maintainInvariant() const {
        if (width_ <= 0 || height_ <= 0) {
            throw std::logic_error("矩形的宽度和高度必须为正数");
        }
    }
    
public:
    Rectangle(double width, double height) : width_(width), height_(height) {
        if (width <= 0 || height <= 0) {
            throw std::invalid_argument("矩形的宽度和高度必须为正数");
        }
        maintainInvariant();  // 确保对象创建时满足不变式
    }
    
    // 修改操作必须保持不变式
    void setWidth(double width) {
        if (width <= 0) {
            throw std::invalid_argument("宽度必须为正数");
        }
        width_ = width;
        maintainInvariant();
    }
    
    void setHeight(double height) {
        if (height <= 0) {
            throw std::invalid_argument("高度必须为正数");
        }
        height_ = height;
        maintainInvariant();
    }
    
    void scale(double factor) {
        if (factor <= 0) {
            throw std::invalid_argument("缩放因子必须为正数");
        }
        width_ *= factor;
        height_ *= factor;
        maintainInvariant();
    }
    
    // 访问器不会破坏不变式
    double getWidth() const { return width_; }
    double getHeight() const { return height_; }
    double getArea() const { return width_ * height_; }
    double getPerimeter() const { return 2 * (width_ + height_); }
    
    void display() const {
        std::cout << "矩形: " << width_ << " x " << height_ 
                  << ", 面积: " << getArea() 
                  << ", 周长: " << getPerimeter() << std::endl;
    }
};

// 复杂不变式示例:有序集合
class SortedSet {
private:
    std::vector<int> data_;
    
    // 不变式:数据必须保持有序且无重复
    void maintainInvariant() const {
        if (!std::is_sorted(data_.begin(), data_.end())) {
            throw std::logic_error("数据必须保持有序");
        }
        
        if (std::adjacent_find(data_.begin(), data_.end()) != data_.end()) {
            throw std::logic_error("数据不能有重复元素");
        }
    }
    
public:
    SortedSet() = default;
    
    bool insert(int value) {
        // 查找插入位置
        auto it = std::lower_bound(data_.begin(), data_.end(), value);
        
        // 如果元素已存在,不插入
        if (it != data_.end() && *it == value) {
            return false;
        }
        
        // 插入到正确位置
        data_.insert(it, value);
        maintainInvariant();
        return true;
    }
    
    bool remove(int value) {
        auto it = std::lower_bound(data_.begin(), data_.end(), value);
        
        if (it != data_.end() && *it == value) {
            data_.erase(it);
            maintainInvariant();
            return true;
        }
        
        return false;
    }
    
    bool contains(int value) const {
        auto it = std::lower_bound(data_.begin(), data_.end(), value);
        return it != data_.end() && *it == value;
    }
    
    size_t size() const { return data_.size(); }
    bool empty() const { return data_.empty(); }
    
    void display() const {
        std::cout << "有序集合: { ";
        for (size_t i = 0; i < data_.size(); ++i) {
            std::cout << data_[i];
            if (i < data_.size() - 1) std::cout << ", ";
        }
        std::cout << " }" << std::endl;
    }
    
    // 提供受控的迭代器访问
    std::vector<int>::const_iterator begin() const { return data_.begin(); }
    std::vector<int>::const_iterator end() const { return data_.end(); }
    
    // 不提供非const迭代器,防止外部破坏不变式
};

int main() {
    std::cout << "=== 不变式和数据完整性 ===" << std::endl;
    
    // 矩形不变式测试
    try {
        Rectangle rect(5.0, 3.0);
        rect.display();
        
        rect.setWidth(8.0);
        rect.display();
        
        rect.scale(1.5);
        rect.display();
        
        // 尝试破坏不变式
        try {
            rect.setWidth(-2.0);  // 应该失败
        } catch (const std::exception& e) {
            std::cout << "捕获异常: " << e.what() << std::endl;
        }
        
    } catch (const std::exception& e) {
        std::cout << "矩形错误: " << e.what() << std::endl;
    }
    
    // 有序集合不变式测试
    std::cout << "\n--- 有序集合测试 ---" << std::endl;
    SortedSet set;
    
    // 插入元素
    std::vector<int> values = {5, 2, 8, 2, 1, 9, 5};
    for (int val : values) {
        bool inserted = set.insert(val);
        std::cout << "插入 " << val << ": " << (inserted ? "成功" : "已存在") << std::endl;
    }
    
    set.display();
    
    // 查找元素
    std::cout << "包含 5: " << (set.contains(5) ? "是" : "否") << std::endl;
    std::cout << "包含 10: " << (set.contains(10) ? "是" : "否") << std::endl;
    
    // 删除元素
    set.remove(2);
    std::cout << "删除 2 后: ";
    set.display();
    
    return 0;
}

总结

封装是面向对象编程的基础原则,通过数据隐藏和接口控制保证代码的安全性和可维护性:

封装要素

  • 数据隐藏:私有成员变量
  • 接口控制:公共方法提供访问
  • 数据验证:确保数据完整性
  • 不变式维护:保持对象状态一致

访问控制级别

级别类内部派生类外部代码用途
private内部实现
protected继承接口
public外部接口

设计原则

  • 最小权限原则:给予最小必要的访问权限
  • 接口稳定性:公共接口变化要谨慎
  • 数据验证:在设置器中验证输入
  • 不变式维护:确保对象状态一致性
  • 高内聚性:类的所有成员围绕核心职责

最佳实践

  • 优先使用private,必要时使用protected
  • 提供清晰的getter/setter接口
  • 在构造函数中建立不变式
  • 通过方法控制状态变化
  • 避免暴露内部数据结构

良好的封装设计让代码更加安全、可维护和可复用,是构建健壮软件系统的重要基础。

本站内容仅供学习和研究使用。