封装是将数据和操作数据的方法捆绑到一个单元中。对外部隐藏对象的内部实现细节仅通过有限受控的接口与外部进行交互。2.1.1 成员权限public、private、protected// public: 任何地方都可以访问。类的内部、子类、类的外部通过对象都可以直接访问public成员。// private: 只能在类的内部访问。子类和类的外部都无法直接访问private成员。// protected: 只能在类的内部和其派生类子类中访问。类的外部无法访问。为继承设计的“半私有”成员。#include iostream#include stringclass BankAccount {// --- 私有部分数据和内部实现 ---private:// 数据成员属性被设为private以保护它们std::string ownerName;double balance;int accountNumber;// 一个私有辅助函数用于内部记录日志外部不需要知道void logTransaction(const std::string action, double amount) {std::cout [LOG] Account accountNumber : action of amount std::endl;}// --- 公共部分对外接口 ---public:// 构造函数用于创建和初始化对象BankAccount(std::string name, int accNum, double initialDeposit) {ownerName name;accountNumber accNum;// 即使是初始存款也通过deposit方法以保证逻辑统一balance 0; // 先设为0deposit(initialDeposit); // 再调用存款方法std::cout Account for ownerName created successfully. std::endl;}// 公共接口存款void deposit(double amount) {if (amount 0) {balance amount;logTransaction(Deposit, amount);} else {std::cout Error: Deposit amount must be positive. std::endl;}}// 公共接口取款void withdraw(double amount) {if (amount 0) {std::cout Error: Withdrawal amount must be positive. std::endl;return;}if (amount balance) {std::cout Error: Insufficient funds. Withdrawal failed. std::endl;} else {balance - amount;logTransaction(Withdrawal, amount);}}// 公共接口查询余额// 注意它返回一个副本而不是balance的引用防止外部通过引用修改double getBalance() const {return balance;}// 公共接口显示账户信息void displayInfo() const {std::cout ------------------------ std::endl;std::cout Account Holder: ownerName std::endl;std::cout Account Number: accountNumber std::endl;std::cout Current Balance: balance std::endl;std::cout ------------------------ std::endl;}}; // 类定义结束int main() {// 创建一个BankAccount对象BankAccount myAccount(Alice, 12345678, 1000.0);myAccount.displayInfo();// 通过公共接口进行操作std::cout \n--- Attempting to deposit 500 --- std::endl;myAccount.deposit(500.0);myAccount.displayInfo();std::cout \n--- Attempting to withdraw 200 --- std::endl;myAccount.withdraw(200.0);myAccount.displayInfo();std::cout \n--- Attempting to withdraw 2000 (insufficient funds) --- std::endl;myAccount.withdraw(2000.0);myAccount.displayInfo();// --- 以下代码是错误的无法通过编译体现了封装的安全性 ---// myAccount.balance 1000000; // 错误double BankAccount::balance is private// myAccount.accountNumber 999; // 错误int BankAccount::accountNumber is private// myAccount.logTransaction(Hacking, 0); // 错误void BankAccount::logTransaction(...) is privatereturn 0;}const关键字void displayInfo() const; 表示这个函数是只读的不会修改对象的内容。构造函数的一种写法class MyClass {private:int secret;public:MyClass(int s) : secret(s) {}// 声明友元函数friend void showSecret(const MyClass obj);};// secret(s)表示将成员secret属性赋值为s2.1.2 友元// 友元函数非成员函数可以访问类的私有成员class MyClass {private:int secret;public:MyClass(int s) : secret(s) {}// 声明友元函数friend void showSecret(const MyClass obj);};// 友元函数定义可以访问 MyClass 的私有成员void showSecret(const MyClass obj) {std::cout Secret: obj.secret std::endl;}int main() {MyClass obj(42);showSecret(obj); // 输出: Secret: 42return 0;}// 友元类一个类的所有成员函数能够访问另一个类的所有私有成员class SecretHolder {private:int data;public:SecretHolder(int d) : data(d) {}// 声明友元类friend class FriendClass;};class FriendClass {public:void accessSecret(const SecretHolder holder) {std::cout Data: holder.data std::endl; // 合法访问}};int main() {SecretHolder holder(100);FriendClass friendObj;friendObj.accessSecret(holder); // 输出: Data: 100return 0;}// 友元成员函数某个类的成员函数可以访问另一个类的私有成员class A {private:int x;public:A(int val) : x(val) {}// 声明类 B 的成员函数为友元friend void B::printX(const A obj);};class B {public:void printX(const A obj) {std::cout A::x obj.x std::endl; // 合法访问}};int main() {A a(10);B b;b.printX(a); // 输出: A::x 10return 0;}2.1.3 构造函数拷贝构造函数默认构造函数带参数的构造函数初始化列表更推荐初始化列表的方式对于类类型成员初始化列表是直接调用其构造函数进行初始化而在构造函数体内赋值则是先调用默认构造函数创建一个临时对象然后再用赋值操作符覆盖。前者效率更高。拷贝构造函数用于创建一个与现有对象完全相同的新对象它的参数是对同类对象的常量引用const ClassName。如果没有定义拷贝构造函数编译器会生成一个默认的拷贝构造函数它会进行浅拷贝。浅拷贝危险性默认的浅拷贝在拷贝默认的指针成员的时候只是拷贝一个指针指针的内容不会被拷贝这会导致调用析构函数的时候可能会重复释放同一块内存。#include cstringclass ShallowCopy {private:char* data;public:ShallowCopy(const char* str) {data new char[strlen(str) 1];strcpy(data, str);}// ~ShallowCopy() { delete[] data; } // 析构函数后面会讲// 默认拷贝构造函数是浅拷贝会导致问题};void problem_demo() {ShallowCopy s1(Hello);ShallowCopy s2 s1; // 调用默认拷贝构造函数s1.data 和 s2.data 指向同一块内存// 当 s1 和 s2 析构时会尝试 delete[] 同一块内存两次导致程序崩溃}深拷贝当类中包含指针成员或动态分配的资源时你必须显式地定义拷贝构造函数自己为新对象分配独立的内存并复制内容这就是深拷贝Deep Copy。class DeepCopy {private:char* data;public:DeepCopy(const char* str) {data new char[strlen(str) 1];strcpy(data, str);}// 显式定义的拷贝构造函数实现深拷贝DeepCopy(const DeepCopy other) {data new char[strlen(other.data) 1]; // 1. 为新对象分配新内存strcpy(data, other.data); // 2. 复制内容std::cout 深拷贝构造函数被调用 std::endl;}~DeepCopy() {delete[] data;}};2.1.4 析构函数释放资源、自动调用、不能重载、先构造的对象后析构后构造的对象先析构。如果在构造函数中使用了new那么就需要在析构函数中显式定义一个析构函数来释放这些资源。如果不定义那么默认的析构函数只会消除指针本身而不会释放指针指向的内存。delete[]操作符专门用于释放new[]创建的动态数组。操作创建单个对象创建对象数组分配内存new intnew int[10]释放内存delete pdelete[] p_arr#include iostream#include cstringclass ResourceHolder {private:char* buffer;int size;public:// 构造函数获取资源ResourceHolder(int s) : size(s) {buffer new char[size]; // 动态分配内存std::cout 构造函数: 分配了 size 字节的内存 std::endl;}// 析构函数释放资源~ResourceHolder() {delete[] buffer; // 释放动态分配的内存std::cout 析构函数: 释放了内存 std::endl;buffer nullptr; // 好习惯避免悬垂指针}void doSomething() {std::cout 正在使用资源... std::endl;}};void createAndDestroy() {std::cout 进入 createAndDestroy 函数 std::endl;ResourceHolder holder(1024); // 创建对象调用构造函数holder.doSomething();std::cout 即将离开 createAndDestroy 函数 std::endl;} // 离开作用域holder 对象被销毁自动调用析构函数int main() {createAndDestroy();std::cout 已回到 main 函数 std::endl;return 0;}