当前位置: 首页 > news >正文

C++面向对象3——C++面向对象的权限、引用与指针

C++面向对象的权限

#include <iostream>using namespace std;class bank{private:double balance;int account_number;string account_name;public:void open_account(string name,double balance,int number);void deposit_money();void withdraw_money();void display_account();bank(){balance = 0;account_number = 0;account_name = ""; }
};void bank::open_account(string name, double bal, int number) {account_name = name;balance = bal;account_number = number;cout << "Account opened successfully!" << endl;display_account();
}void bank::deposit_money() {double amount;cout << "Enter amount to deposit: ";cin >> amount;balance += amount;cout << "Deposit successful!" << endl;display_account();
}void bank::withdraw_money() {double amount;cout << "Enter amount to withdraw: ";cin >> amount;if (amount > balance) {cout << "Insufficient balance!" << endl;} else {balance -= amount;cout << "Withdrawal successful!" << endl;}display_account();
}void bank::display_account() {cout << "Account Name: " << account_name << endl;cout << "Account Number: " << account_number << endl;cout << "Balance: " << balance << endl;
}int main()
{bank b;b.open_account("John Doe", 1000.0, 123456);b.deposit_money();b.withdraw_money(); b.display_account();return 0;
}

这段代码通过访问权限(privatepublic)实现了面向对象编程的封装特性。以下是详细分析:

1. 访问权限设计

class bank{private:double balance;        // 账户余额int account_number;    // 账户号码string account_name;   // 账户名public:// 公有方法(接口)void open_account(...);void deposit_money();void withdraw_money();void display_account();bank();  // 默认构造函数
};
  • private 成员
    • balanceaccount_numberaccount_name 是核心数据,被声明为 private
    • 外部无法直接访问或修改这些数据,只能通过类的公有方法操作。
  • public 方法
    • 提供了用户与账户交互的唯一途径(如开户、存款、取款)。
    • 确保数据的完整性和安全性(例如,取款时检查余额)。

2. 封装的优势

① 数据保护
  • 外部无法直接修改 balance
    bank b;
    b.balance = 1000000;  // 错误!private 成员不可访问
    
  • 必须通过 deposit_money()withdraw_money() 操作余额,确保业务逻辑(如余额检查)被执行。
② 实现细节隐藏
  • 用户不需要知道 balance 如何存储或计算,只需调用 display_account() 查看信息。
  • 未来若需要修改内部实现(如改用 long long 存储金额),只需调整类的内部代码,外部调用无需改变。

3. 方法的访问权限控制

构造函数 bank()
  • 初始化所有 private 成员为默认值,防止未初始化的数据被使用。
open_account(...)
  • 负责安全地初始化账户信息,避免外部直接设置 private 成员。
  • 例如:account_number 可能需要遵循特定格式或唯一性规则,通过方法封装可确保规则被执行。
deposit_money()
  • 验证存款金额的合法性(虽然当前代码未检查负数,但可在此处添加逻辑)。
  • 直接修改 balance 会绕过验证逻辑。
withdraw_money()
  • 关键业务逻辑:检查余额是否充足。
  • 若允许外部直接修改 balance,可能导致透支(如 balance = -1000)。

4. 潜在改进建议

① 增加只读访问器(Getter)
public:double get_balance() const { return balance; }int get_account_number() const { return account_number; }string get_account_name() const { return account_name; }
  • 优势:允许外部读取数据但禁止修改,增强封装性。
  • const 修饰:确保方法不修改对象状态。
② 限制构造函数参数
public:bank(string name, double initial_balance, int number) {open_account(name, initial_balance, number);}
  • 优势:强制用户在创建对象时提供必要信息,避免创建无效账户。
③ 异常处理
void withdraw_money() {double amount;cout << "Enter amount to withdraw: ";cin >> amount;if (amount <= 0 || amount > balance) {throw invalid_argument("Invalid withdrawal amount");  // 抛出异常}balance -= amount;cout << "Withdrawal successful!" << endl;display_account();
}
  • 优势:更健壮的错误处理,避免程序崩溃。

5. 总结

  • 封装原则:通过 private 数据和 public 方法,确保数据只能通过受控接口访问。
  • 安全性:防止外部直接操作敏感数据(如余额),降低错误和恶意修改的风险。
  • 可维护性:内部实现可以独立修改,不影响外部代码。

该代码通过合理的访问权限设计,实现了一个安全且易于使用的银行账户类。

引用与指针的区别

在C++中,引用(Reference)指针(Pointer) 都用于间接访问对象,但它们的语法、语义和使用场景有本质区别。以下是核心辨析:

1. 基本语法与定义

指针
int x = 10;
int* ptr = &x;  // 指针存储变量的地址
*ptr = 20;      // 通过解引用修改x的值
  • 本质:变量,存储内存地址。
  • 声明:使用 *(如 int* ptr)。
  • 操作:通过 & 取地址,* 解引用。
引用
int x = 10;
int& ref = x;   // 引用是x的别名
ref = 20;       // 直接修改x的值
  • 本质:对象的别名,与原变量绑定到同一内存地址。
  • 声明:使用 &(如 int& ref)。
  • 操作:直接使用引用名,无需显式解引用。

2. 核心区别

特性指针引用
是否可空可以为 nullptr(空指针)必须初始化,不能为 nullptr
是否可变可以指向不同对象(可重新赋值)一旦初始化,不能更改绑定对象
内存占用通常占4/8字节(取决于架构)不占额外内存(编译器实现可能不同)
解引用语法需要显式使用 *(如 *ptr直接使用引用名(如 ref
多级间接性支持多级指针(如 int** pp不支持多级引用
自增/自减语义移动指针地址(如 ptr++修改绑定对象的值(如 ref++

3. 使用场景对比

指针的典型场景
  • 动态内存分配
    int* ptr = new int(10);  // 必须用指针管理堆内存
    delete ptr;
    
  • 需要重新指向不同对象
    void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
    }
    
  • 允许空值语义
    void process(int* ptr) {if (ptr != nullptr) {  // 必须检查空指针// ...}
    }
    
引用的典型场景
  • 函数参数传递(避免拷贝)
    void print(const std::string& str) {  // 常量引用避免拷贝std::cout << str << std::endl;
    }
    
  • 操作符重载(如 =[]
    std::vector<int> vec = {1, 2, 3};
    vec[0] = 10;  // operator[] 返回引用,允许修改原对象
    
  • 范围for循环
    for (int& num : vec) {  // 引用允许修改容器元素num *= 2;
    }
    

4. 安全性与陷阱

指针的风险
  • 空指针解引用
    int* ptr = nullptr;
    *ptr = 10;  // 运行时崩溃!
    
  • 野指针
    int* ptr = new int(10);
    delete ptr;
    *ptr = 20;  // 访问已释放的内存
    
  • 内存泄漏
    void leak() {int* ptr = new int(10);// 未delete ptr
    }
    
引用的风险
  • 悬挂引用
    int& ref = *new int(10);  // 引用堆对象
    delete &ref;
    ref = 20;  // 访问已释放的内存
    
  • 隐式生命周期延长(临时对象)
    const std::string& ref = "hello";  // 临时对象的引用
    // 语句结束后临时对象销毁,ref变为悬挂引用
    

5. 何时选择指针或引用?

场景优先选择指针优先选择引用
需要表示“无对象”状态(空值)❌(引用必须绑定对象)
需要重新指向不同对象❌(引用不可变)
实现数据结构(如链表、树)✅(节点通过指针连接)
函数参数需要默认值✅(如 void func(int* p = nullptr)
重载操作符(如 operator[]✅(返回引用允许赋值)
避免拷贝大型对象✅(指针传递)✅(引用传递)

6. C++11 后的改进

  • 智能指针(如 std::unique_ptrstd::shared_ptr

    std::unique_ptr<int> ptr = std::make_unique<int>(10);  // 自动内存管理
    
    • 优势:避免手动 delete,防止内存泄漏。
  • 右值引用(&&

    void func(std::string&& str) {  // 接收右值引用(临时对象)// 可用于移动语义
    }
    
    • 优势:实现高效的资源转移(如 std::vectorpush_back)。

总结

  • 指针:灵活但危险,适合动态内存管理、需要空值或重新赋值的场景。
  • 引用:安全且简洁,适合作为函数参数或返回值,避免拷贝并提供自然的语法。

合理选择引用和指针,可以提高代码的安全性、可读性和性能。

一个使用引用修改函数值的例子

#include <iostream>
using namespace std;int a[4]={1,2,3,4};int& set_a(int i){return a[i];
}
int main()
{int b = set_a(0);int& c = set_a(1);int d = 9;set_a(3) = d;cout << "b: " << b << endl;cout << "c: " << c << endl;for (int i = 0; i < 4; i++){cout << a[i] <<endl;}c=10;cout << "c: " << c << endl;for (int i = 0; i < 4; i++){cout << a[i] <<endl;}int* p;p=&c;*p=999;cout << "p: " << *p << endl;for (int i = 0; i < 4; i++){cout << a[i] <<endl;}return 0;
}

这段代码通过引用(Reference)直接操作数组元素,展示了引用的强大之处。以下是详细分析:

1. 核心函数 set_a(int i)

int& set_a(int i){return a[i];  // 返回全局数组a的第i个元素的引用
}
  • 功能:返回数组 a 中索引为 i 的元素的引用(即内存地址的别名)。
  • 关键:通过返回引用,外部代码可直接修改原数组元素。

2. 变量初始化与引用绑定

int b = set_a(0);
  • set_a(0) 返回 a[0] 的引用(值为 1)。
  • b 是值拷贝b 被初始化为 1,后续修改 a[0] 不会影响 b
int& c = set_a(1);
  • set_a(1) 返回 a[1] 的引用。
  • c 直接绑定到 a[1]ca[1] 的别名,修改 c 等同于修改 a[1]

3. 通过引用修改数组元素

set_a(3) = d;
  • 等价于 a[3] = d(即 a[3] = 9)。
  • 输出验证
    b: 1        // b保持初始值1
    c: 2        // c绑定到a[1],当前值为2
    a[0]: 1
    a[1]: 2
    a[2]: 3
    a[3]: 9     // 被修改为9
    
c = 10;
  • 直接修改 a[1](因为 ca[1] 的引用)。
  • 输出验证
    c: 10       // c的值变为10
    a[0]: 1
    a[1]: 10    // 被修改为10
    a[2]: 3
    a[3]: 9
    
③ 通过指针修改 c
int* p = &c;  // p指向c(即a[1]的地址)
*p = 999;     // 修改p指向的内存(即a[1])
  • 输出验证
    p: 999      // p指向的值为999
    a[0]: 1
    a[1]: 999   // 被修改为999
    a[2]: 3
    a[3]: 9
    

4. 内存与引用关系图

全局数组 a:
+---+-------+---+---+
| 1 |2→999  | 3 | 9 |
+---+-------+---+---+↑     ↑         ↑|     ↓         |b    c,p       d=9
  • b:独立变量,值为 1a[0] 的初始值)。
  • cp:均指向 a[1] 的内存地址,修改它们会直接影响 a[1]

5. 潜在风险与注意事项

① 越界访问
set_a(10) = 5;  // 访问a[10],导致未定义行为!
  • 原因set_a 未检查索引合法性,可能访问非法内存。
② 悬挂引用
int& ref = set_a(0);  // 绑定到a[0]
// 如果后续a被销毁或重新分配,ref将变为悬挂引用

6. 改进建议

① 添加边界检查
int& set_a(int i) {if (i < 0 || i >= 4) {throw std::out_of_range("Index out of range");}return a[i];
}
② 使用更安全的容器(如 std::vector
#include <vector>
std::vector<int> a = {1, 2, 3, 4};int& set_a(int i) {return a.at(i);  // at()方法会自动检查边界
}

7. 关键知识点

  1. 引用的本质:对象的别名,与原对象共享同一内存地址。
  2. 返回引用的函数:可用于直接修改目标对象(如数组元素、类成员)。
  3. 引用与指针的关系:引用一旦绑定无法更改,而指针可重新指向其他对象。

通过引用,代码可以实现简洁且高效的内存操作,但需谨慎处理边界和生命周期问题。

相关文章:

  • 福州网站设计软件公司/如何做电商新手入门
  • 工作号做文案素材的网站/外包项目接单平台
  • 交友网站开发/百度客服人工在线咨询电话
  • 湛江网站优化/外贸建站推广哪家好
  • 网站建设 知识库/百度提交入口网站
  • 济南电视台在线直播/湖北网站seo策划
  • H5新增属性
  • logstash读取kafka日志写到oss归档存储180天
  • 2025年CSS最新高频面试题及核心解析
  • 边缘-云协同智能视觉系统:实时计算与云端智能的融合架构
  • Linux系统能ping通ip但无法ping通域名的解决方法
  • LeetCode热题100—— 160. 相交链表
  • [附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+Vue实现的校园二手交易平台管理系统,推荐!
  • Threejs实现 3D 看房效果
  • 【kubernetes】--controller(deployment)
  • 洛谷 P10379 [GESP202403 七级] 俄罗斯方块-普及/提高-
  • 使用Vue重新构建应用程序
  • UP COIN:从 Meme 共识走向公链与 RWA 的多元生态引擎
  • 浅析std::atomic<T>::compare_exchange_weak和std::atomic<T>::compare_exchange_strong
  • Docker 与 Containerd 交互机制简单剖析
  • Apache SeaTunnel Spark引擎执行流程源码分析
  • 刀客doc:阿里巴巴集团品牌部划归集团公关管理
  • 数组题解——​轮转数组【LeetCode】
  • [HTML]iframe显示pdf,隐藏左侧分页
  • 线程池 JMM 内存模型
  • 【题解-Acwing】1022. 宠物小精灵之收服