C++学习笔记——友元及重载运算符

目录

一、友元

1.1声明友元函数

1.2声明友元类

二、运算符重载

2.1重载加号运算符

2.2重载流插入运算符

三、一个简单的银行管理系统

四、 详细的介绍


一、友元

在 C++ 中,友元是一个函数或类,它可以访问另一个类的私有成员或保护成员。通常情况下,我们在类定义中声明友元函数或友元类,以便它们能够访问类的私有成员。友元可以是一个函数、类、或整个命名空间。

1.1声明友元函数

首先,我们来看一下如何声明友元函数。假设我们有一个 Box 类,它表示一个三维立方体:

class Box {
public:Box(double l, double w, double h) : length(l), width(w), height(h) {}private:double length;double width;double height;
};

我们想要计算一个 Box 对象的体积,但是 lengthwidthheight 都是私有成员,无法在类外直接访问。这时候,我们可以使用友元函数来实现。我们可以在类定义中声明一个友元函数,并将其定义为一个全局函数:

class Box {
public:Box(double l, double w, double h) : length(l), width(w), height(h) {}friend double getVolume(Box box);private:double length;double width;double height;
};double getVolume(Box box) {return box.length * box.width * box.height;
}

在上面的代码中,我们在 Box 类定义中声明了一个友元函数 getVolume(),并将其定义为一个全局函数。在 getVolume() 函数中,我们可以直接访问 Box 类的私有成员 lengthwidthheight,计算出它的体积并返回。

1.2声明友元类

除了友元函数,我们还可以声明友元类。假设我们有一个 Stack 类,它表示一个栈:

template <typename T>
class Stack {
public:Stack() : top(-1) {}private:T data[10];int top;friend class StackIterator<T>;
};

我们想要实现一个迭代器类来遍历栈中的元素。但是,由于 datatop 都是私有成员,无法在迭代器类中直接访问。这时候,我们可以声明 StackIterator 类为 Stack 类的友元类:

template <typename T>
class StackIterator {
public:StackIterator(Stack<T>& s) : stack(s), index(s.top) {}T next() {return stack.data[index--];}private:Stack<T>& stack;int index;
};template <typename T>
class Stack {
public:Stack() : top(-1) {}friend class StackIterator<T>;private:T data[10];int top;
};

在上面的代码中,我们声明了一个友元类 StackIterator,并在 Stack 类定义中将其声明为友元类。在 StackIterator 类中,我们可以直接访问 Stack 类的私有成员 datatop,实现了对栈的迭代。

二、运算符重载

C++ 中有一些运算符是可以被重载的,比如加号、减号、乘号、除号等等。通过重载运算符,我们可以改变它的行为,使其能够作用于我们自定义的类型。

2.1重载加号运算符

假设我们有一个 Complex 类,它表示一个复数:

class Complex {
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}private:double real;double imag;
};

我们想要实现复数的加法运算,但是加号运算符无法直接作用于自定义的类型。这时候,我们可以重载加号运算符 +,使其能够对两个 Complex 对象进行相加,并返回一个新的 Complex 对象:

class Complex {
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}Complex operator+(const Complex& other) const {return Complex(real + other.real, imag + other.imag);}private:double real;double imag;
};

在上面的代码中,我们重载了加号运算符 +,并定义了一个成员函数 operator+()。在函数中,我们定义了一个新的 Complex 对象,其实部为两个复数的实部相加,虚部为两个复数的虚部相加,并将其返回。

2.2重载流插入运算符

除了加号运算符,我们还可以重载其他运算符。比如,我们可以重载流插入运算符 <<,使其能够输出我们自定义的类型。假设我们有一个 Person 类,它表示一个人:

#include <iostream>
#include <string>class Person {
public:Person(std::string n, int a) : name(n), age(a) {}friend std::ostream& operator<<(std::ostream& os, const Person& p);private:std::string name;int age;
};std::ostream& operator<<(std::ostream& os, const Person& p) {os << "Name: " << p.name << ", Age: " << p.age;return os;
}int main() {Person p("Alice", 20);std::cout << p << std::endl;return 0;
}

在上面的代码中,我们重载了流插入运算符 <<,并将其定义为一个友元函数。在函数中,我们将 Person 对象的姓名和年龄输出到标准输出流 os 中,然后返回该流。在主函数中,我们创建了一个 Person 对象 p,并使用 << 运算符将其输出到标准输出流中。

需要注意的是,在重载运算符时需要遵循一些规则。比如,我们必须使用正确的参数类型和返回值类型,以及保证运算符的语义符合预期。此外,C++ 中还存在一些运算符是无法被重载的,比如条件运算符 ?:,作用域运算符 :: 等等。

三、一个简单的银行管理系统

#include <iostream>
#include <string>
#include <vector>class Account {
public:Account(std::string name, std::string accountNumber, double balance): name(name), accountNumber(accountNumber), balance(balance) {}void deposit(double amount) {balance += amount;std::cout << "Deposit successful. New balance: " << balance << std::endl;}void withdraw(double amount) {if (balance >= amount) {balance -= amount;std::cout << "Withdrawal successful. New balance: " << balance << std::endl;} else {std::cout << "Insufficient balance." << std::endl;}}void displayInfo() {std::cout << "Name: " << name << std::endl;std::cout << "Account Number: " << accountNumber << std::endl;std::cout << "Balance: " << balance << std::endl;}private:std::string name;std::string accountNumber;double balance;
};class Bank {
public:void createAccount(std::string name, std::string accountNumber, double balance) {Account account(name, accountNumber, balance);accounts.push_back(account);std::cout << "Account created successfully." << std::endl;}void deposit(std::string accountNumber, double amount) {Account* account = findAccount(accountNumber);if (account != nullptr) {account->deposit(amount);} else {std::cout << "Account not found." << std::endl;}}void withdraw(std::string accountNumber, double amount) {Account* account = findAccount(accountNumber);if (account != nullptr) {account->withdraw(amount);} else {std::cout << "Account not found." << std::endl;}}void displayAllAccounts() {for (const auto& account : accounts) {account.displayInfo();std::cout << "---------------------------" << std::endl;}}private:std::vector<Account> accounts;Account* findAccount(std::string accountNumber) {for (auto& account : accounts) {if (account.getAccountNumber() == accountNumber) {return &account;}}return nullptr;}
};int main() {Bank bank;int choice;do {std::cout << "1. Create Account" << std::endl;std::cout << "2. Deposit" << std::endl;std::cout << "3. Withdraw" << std::endl;std::cout << "4. Display All Accounts" << std::endl;std::cout << "5. Exit" << std::endl;std::cout << "Enter your choice: ";std::cin >> choice;if (choice == 1) {std::string name, accountNumber;double balance;std::cout << "Enter name: ";std::cin >> name;std::cout << "Enter account number: ";std::cin >> accountNumber;std::cout << "Enter initial balance: ";std::cin >> balance;bank.createAccount(name, accountNumber, balance);} else if (choice == 2) {std::string accountNumber;double amount;std::cout << "Enter account number: ";std::cin >> accountNumber;std::cout << "Enter amount to deposit: ";std::cin >> amount;bank.deposit(accountNumber, amount);} else if (choice == 3) {std::string accountNumber;double amount;std::cout << "Enter account number: ";std::cin >> accountNumber;std::cout << "Enter amount to withdraw: ";std::cin >> amount;bank.withdraw(accountNumber, amount);} else if (choice == 4) {bank.displayAllAccounts();}} while (choice != 5);return 0;
}

在上面的代码中,我们定义了两个类:AccountBankAccount 类代表一个银行账户,具有姓名、账号和余额属性,以及存款、取款和展示信息的方法。Bank 类代表整个银行系统,具有创建账户、存款、取款和显示所有账户的方法。其中,存款和取款的操作会调用 Account 类相应的方法。

在主函数中,我们通过简单的菜单来模拟用户与银行系统的交互。用户可以选择创建账户、存款、取款或显示所有账户的操作,直到选择退出。

四、 详细的介绍

以下是代码中每个部分的更详细解释:

class Account {
public:Account(std::string name, std::string accountNumber, double balance): name(name), accountNumber(accountNumber), balance(balance) {}

Account 类的构造函数中,我们使用传入的参数来初始化私有成员变量 nameaccountNumberbalance

    void deposit(double amount) {balance += amount;std::cout << "Deposit successful. New balance: " << balance << std::endl;}

deposit 方法用于存款操作。它接受一个 double 类型的参数 amount,代表要存入的金额。方法内部,我们将传入的金额加到余额上,并输出新的余额。

    void withdraw(double amount) {if (balance >= amount) {balance -= amount;std::cout << "Withdrawal successful. New balance: " << balance << std::endl;} else {std::cout << "Insufficient balance." << std::endl;}}

withdraw 方法用于取款操作。它接受一个 double 类型的参数 amount,代表要取出的金额。方法内部,我们首先检查账户余额是否足够,如果足够就从余额中减去取款金额,并输出新的余额。如果余额不足,则输出错误信息。

    void displayInfo() {std::cout << "Name: " << name << std::endl;std::cout << "Account Number: " << accountNumber << std::endl;std::cout << "Balance: " << balance << std::endl;}

displayInfo 方法用于展示账户的信息。它输出账户的姓名、账号和余额。

class Bank {
public:void createAccount(std::string name, std::string accountNumber, double balance) {Account account(name, accountNumber, balance);accounts.push_back(account);std::cout << "Account created successfully." << std::endl;}

createAccount 方法用于创建一个新的账户。它接受三个参数:姓名、账号和初始余额。方法内部,我们创建一个新的 Account 对象,并将其添加到 accounts 列表中。然后,我们输出成功创建账户的消息。

    void deposit(std::string accountNumber, double amount) {Account* account = findAccount(accountNumber);if (account != nullptr) {account->deposit(amount);} else {std::cout << "Account not found." << std::endl;}}

deposit 方法用于给指定账户存款。它接受两个参数:账户号码和存款金额。方法内部,我们通过调用 findAccount 方法来查找要操作的账户。如果找到了账户,则调用该账户的 deposit 方法执行存款操作。如果未找到账户,则输出错误信息。

    void withdraw(std::string accountNumber, double amount) {Account* account = findAccount(accountNumber);if (account != nullptr) {account->withdraw(amount);} else {std::cout << "Account not found." << std::endl;}}

withdraw 方法用于给指定账户取款。它接受两个参数:账户号码和取款金额。方法内部,我们通过调用 findAccount 方法来查找要操作的账户。如果找到了账户,则调用该账户的 withdraw 方法执行取款操作。如果未找到账户,则输出错误信息。

    void displayAllAccounts() {for (const auto& account : accounts) {account.displayInfo();std::cout << "---------------------------" << std::endl;}}

displayAllAccounts 方法用于展示银行中所有账户的信息。它遍历 accounts 列表,并对每个账户调用 displayInfo 方法来输出信息。在每个账户信息输出之后,我们打印分隔线以区分不同的账户。

int main() {Bank bank;int choice;do {std::cout << "1. Create Account" << std::endl;std::cout << "2. Deposit" << std::endl;std::cout << "3. Withdraw" << std::endl;std::cout << "4. Display All Accounts" << std::endl;std::cout << "5. Exit" << std::endl;std::cout << "Enter your choice: ";std::cin >> choice;if (choice == 1) {std::string name, accountNumber;double balance;std::cout << "Enter name: ";std::cin >> name;std::cout << "Enter account number: ";std::cin >> accountNumber;std::cout << "Enter initial balance: ";std::cin >> balance;bank.createAccount(name, accountNumber, balance);} else if (choice == 2) {std::string accountNumber;double amount;std::cout << "Enter account number: ";std::cin >> accountNumber;std::cout << "Enter amount to deposit: ";std::cin >> amount;bank.deposit(accountNumber, amount);} else if (choice == 3) {std::string accountNumber;double amount;std::cout << "Enter account number: ";std::cin >> accountNumber;std::cout << "Enter amount to withdraw: ";std::cin >> amount;bank.withdraw(accountNumber, amount);} else if (choice == 4) {bank.displayAllAccounts();}} while (choice != 5);return 0;
}

main 函数中,我们使用一个循环来模拟用户与银行系统的交互。我们显示一个简单的菜单,让用户选择不同的操作。根据用户的选择,我们调用 Bank 类的相应方法来执行操作。当用户选择退出时,循环结束,程序终止。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/603984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uView Alert 提示

用于页面中展示重要的提示信息。 基础用法# Alert 组件不属于浮层元素&#xff0c;不会自动消失或关闭。 Alert 组件提供四种类型&#xff0c;由 type 属性指定&#xff0c;默认值为 info。 success alert info alert warning alert error alert 主题# Alert 组件提供了…

HTML小白入门基础(概述,结构与基本常用标签)

目录 一、什么是HTML 二、HTML的基本结构&#xff1a; 三、结构与属性&#xff1a; 四、常见标签&#xff1a; 一、什么是HTML HTML&#xff1a;超文本标记语言(HyperText Markup Language) 超文本&#xff1a;指的是网页中可以显示的内容(图片&#x…

【Python机器学习】基于随机森林全球经济危机预测

一、引言 全球经济危机是一个复杂的问题,受到多种因素的影响,如金融市场、政策环境、地缘政治等。预测经济危机对于政策制定者、投资者和企业来说至关重要,因为它可以帮助他们提前做出应对措施,减少潜在的损失。然而,准确预测经济危机是一项具有挑战性的任务,因为涉及到…

【LeetCode739】每日温度

1、题目描述 【题目链接】 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。…

ROS+moveit+jakaminicob仿真运动

先浅浅的放一个官方的c文档&#xff1a; Motion Planning API — moveit_tutorials Melodic documentation 目录 一、实现运动到目标点的程序 二、在rviz里面新建扫描平台 一、实现运动到目标点的程序 &#xff08;等我得空了补一个c运行环境部署说明&#xff09; #inclu…

【Linux】CentOS 7重装保留数据的方法

我们需要重装CentOS 7系统&#xff0c;但是又想保留原来的数据。这篇文章将会从多个方面详细介绍如何重装CentOS 7系统&#xff0c;同时又能保留原有的数据。 一、备份重要数据 在重装CentOS 7系统之前&#xff0c;我们需要备份我们的重要数据。这可以通过多种方式实现&#…

React16源码: React中创建更新的方式及ReactDOM.render的源码实现

React当中创建更新的主要方式 ReactDOM.render || hydrate 这两个API都是我们要把整个应用第一次进行渲染到我们的页面上面能够展现出来我们整个应用的样子的一个过程这是初次渲染 setState 后续更新应用 forceUpdate 后续更新应用 replaceState 在后续被舍弃 关于 ReactDOM…

Qt undefined reference to `vtable for xxx‘

一、问题背景 在编译QT代码时&#xff0c;出现 undefined reference to xxx::entered()&#xff0c;通过鼠标双击QtCreator“问题栏”中的该行&#xff0c;则会跳转到发送信号的代码所在行。与上述代码一同出现在“问题栏”的还有 undefined reference to vtable for xxx’。 …

Git常用命令diff和mv

Git常用命令diff和mv 1、diff # 查看工作区和暂存区所有文件的对比 # 该命令可以显示尚未添加到stage的文件的变更 $ git diff# 查看工作区和暂存区单个文件的对比 $ git diff file# 显示暂存区和上一个commit的差异 # 查看暂存区与指定提交版本的不同,版本可缺省为HEAD $ gi…

力扣(leetcode)第412题Fizz Buzz(Python)

412.Fizz Buzz 题目链接&#xff1a;412.Fizz Buzz 给你一个整数 n &#xff0c;找出从 1 到 n 各个整数的 Fizz Buzz 表示&#xff0c;并用字符串数组 answer&#xff08;下标从 1 开始&#xff09;返回结果&#xff0c;其中&#xff1a; answer[i] “FizzBuzz” 如果 i 同…

Linux-文件系统管理实验2

1、将bin目录下的所有文件列表放到bin.txt文档中&#xff0c;并将一共有多少个命令的结果信息保存到该文件的最后一行。统计出文件中以b开头的所有命令有多少个&#xff0c;并将这些命令保存到b.txt文档中。将文档中以p结尾的所有命令保存到p.txt文件中&#xff0c;并统计有多少…

lv14 ioctl、printk及多个此设备支持 6

1 ioctl操作实现 对相应设备做指定的控制操作&#xff08;各种属性的设置获取等等&#xff09; long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg); 功能&#xff1a;对相应设备做指定的控制操作&#xff08;各种属性的设置获取等等&#xff09; 参数…

【csharp】依赖注入

依赖注入 依赖注入&#xff08;Dependency Injection&#xff0c;DI&#xff09;是一种软件设计模式&#xff0c;旨在降低组件之间的耦合度。在依赖注入中&#xff0c;一个类的依赖关系不是在类内部创建&#xff0c;而是通过外部传递进来。这通常通过构造函数、方法参数或属性…

氢燃料电池技术综述

文章目录 工作原理 系统集成 应用 特点 国家政策 行业发展 机遇和挑战 参考文献 工作原理 氢燃料电池是通过催化剂将氢气和氧气反应生成电能和水的过程&#xff0c;在这个过程中会伴随有热量产生。 系统集成 氢燃料电池需要将氢气供应系统、氧气供应系统、电堆、冷却系…

【基础篇】十二、引用计数法 可达性分析算法

文章目录 1、Garbage Collection2、方法区的回收3、堆对象回收4、引用计数法5、可达性分析算法6、查看GC Root对象 1、Garbage Collection C/C&#xff0c;无自动回收机制&#xff0c;对象不用时需要手动释放&#xff0c;否则积累导致内存泄漏&#xff1a; Java、C#、Python、…

Linux程序、进程以及计划任务(第一部分)

目录 一、程序和进程 1、什么是程序&#xff1f; 2、什么是进程&#xff1f; 3、线程是什么&#xff1f; 4、如何查看是多线程还是单线程 5、进程结束的两种情况&#xff1a; 6、进程的状态 二、查看进程信息的相关命令 1、ps&#xff1a;查看静态的进程统计信息 2、…

c++基础(对c的扩展)

文章目录 命令空间引用基本本质引用作为参数引用的使用场景 内联函数引出基本概念 函数补充默认参数函数重载c中函数重载定义条件函数重载的原理 命令空间 定义 namespace是单独的作用域 两者不会相互干涉 namespace 名字 { //变量 函数 等等 }eg namespace nameA {int num;v…

判断对象是否是垃圾的引用计数法有什么问题

给对象中添加一个引用计数器&#xff0c;每当有一个地方引用它&#xff0c;计数器就加一&#xff0c;当引用失效计数器就减一&#xff0c;任何时候引用计数器为0的对象就是不可能再被使用的&#xff08;变成垃圾&#xff09;。 这个方法实现简单、效率高&#xff0c;但是目前主…

STM32F407ZGT6-flash地址-SRAM

2、 2-STM32F407英文数据手册&#xff08;没有中文&#xff09;.pdf Memory mapping

抽丝剥茧设计模式

Singleton 单例 饿汉式 最简单的方式 /*** 饿汉式* 类加载到内存后&#xff0c;就实例化一个单例&#xff0c;JVM保证线程安全* 简单实用&#xff0c;推荐使用&#xff01;* 唯一缺点&#xff1a;不管用到与否&#xff0c;类装载时就完成实例化* Class.forName(""…