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接口
- 在构造函数中建立不变式
- 通过方法控制状态变化
- 避免暴露内部数据结构
良好的封装设计让代码更加安全、可维护和可复用,是构建健壮软件系统的重要基础。