C++的第十一天笔记

应尽可能的使用const

  • 使用const可以避免无意中修改数据的编程错误;

  • 使用const使函数能够处理const和非const实参,否则只能接收非const数据(前提:函数参数为引用 / 指针);

  • 使用const引用使函数能够正确生成并使用生成的临时变量。

将引用用于结构

结构 / 类的对象往往体积较大,值传递会触发浅拷贝深拷贝

  • 浅拷贝:仅复制成员变量的表面值(如指针)。可能导致内存被多次释放( C++ 默认)。

  • 深拷贝:会递归复制原始对象及其所有嵌套对象,生成一个完全独立的新对象。对内存的开销大(需要手动编写拷贝构造函数实现)。

指针操作繁琐可读性差,容易产生野指针,比较危险。

引用共用空间,无拷贝;语法简洁,可读性高;无空野指针,安全。

引用函数(和指针函数类似)
复制代码
 //语法:数据类型& 函数名(形参列表); 

引用可以作为函数返回值,可以避免拷贝,直接操作对象。

复制代码
 // 结构体定义(投篮数据)
 struct free_throws {
     string name;
     int made;
     int attempts;
 };
 ​
 //引用函数,用于更新数据
 free_throws& accumulate(free_throws& target, const free_throws& source) {
     // 累加投中次数和尝试次数
     target.attempts += source.attempts;
     target.made += source.made;
     
     return target;
 }
 ​
 ...
  // 初始化三个球员的数据
     free_throws player1 = {"库里", 45, 50,};
     free_throws player2 = {"汤普森", 38, 40};
     free_throws team_total = {"勇士队合计", 0,0};
  // 链式调用:将 player1 和 player2 的数据累加到 team_total
     accumulate(accumulate(team_total, player1), player2); 
  //也可以这样用
     accumulate(team_total, player1).attempts = 3;

accumulate(accumulate(team_total, player1), player2);

**引用函数可以实现链式操作。**使用无返回值函数可以操作同一片空间,但无法实现链式操作。使用值返回函数,编译器会创建临时变量,操作的不是同一片空间,不仅无效,而且创建副本对内存开销很大。

accumulate(team_total, player1).attempts = 3;

引用函数的返回值可以做左值,因为其和原数据共享空间,空间固定。倘若是值传递,由于是临时数据,编译器会报错。

将引用用于类对象

将类对象传递给函数时,C++通常的做法是使用引用。

C++里面定义了一种char*到string的转换功能,所以可以使用C风格的字符串来初始化string对象。

在传参时,引用参数是 const 类型,实参类型不正确,且可以隐式转换为形参类型,会创建临时对象,使用的是临时空间。
注:千万不要返回局部的引用,返回时引用已被销毁,执意使用编译器会警告不会报错,但程序试图执行这个引用函数的时候,程序会崩溃。

对象、继承和引用

对象是类的实例,可以通过成员函数操作。

继承是能够将特性从一个类传递到另一个类的语言特性。 派生类(子)会继承基类(父)的方法,比如基类有 name,派生类不用再定义,直接能用;

基类的引用可以指向派生类的对象。而且无需强制类型转换。

复制代码
     ...
     ofstream fout;    // 1. 创建派生类对象:ofstream是ostream的派生类,用于向文件写数据
     const char * fn = "ep-data.txt";  // 要写入的文件名(C风格字符串)
     fout.open(fn);    // 2. 打开文件:关联fout对象和"ep-data.txt"文件
     
      double objective;  // 存储望远镜物镜的焦距(单位:mm)
     // 提示用户输入物镜焦距
     cout << "请输入你的望远镜物镜焦距(单位:mm):";
     cin >> objective;  // 读取用户输入的物镜焦距
     
      // ostream& os(基类引用)直接绑定fout,无需强转 → 数据写入文件
     file_it(fout, objective);
     
     cout << "数据已写入文件!" << endl;
     fout.close();//关闭文件
     ...
复制代码
 // os:基类引用,绑定派生类对象(fout或cout),实现通用输出
 void file_it(ostream & os, double fo)
 {
     ios_base::fmtflags initial;  // 存储输出流的“初始格式状态”(用于后续恢复)
     initial = os.setf(ios_base::fixed);  // 设置输出为“固定小数格式”,并保存初始格式
 ​
     os.precision(0);  // 设置小数位数为0(整数输出)
     os << "物镜焦距:" << fo << " mm\n";  // 输出物镜焦距(os绑定fout则写文件,绑定cout则写屏幕)
 ​
     os.setf(ios::showpoint);  // 强制显示小数点(即使是整数也显示 .0)
     os.precision(1);          // 设置小数位数为1(目镜焦距保留1位小数)
 ​
     os.width(12);  // 设置下一个输出内容的宽度为12字符(右对齐,默认填充空格)
     os << "目镜焦距(mm)";  // 输出表头1
 ​
     os.setf(initial);  // 恢复输出流的初始格式(避免影响后续其他输出)
 }

ofstreamcout拥有 这些格式控制功能,os 作为基类引用,能直接调用这些继承来的功能。简单来讲就是:辈分高的类(父类)的引用/指针,可以接收(例如形参接收实参)辈分低的类(子类)的对象,接收后,这个子类对象可以使用父类的方法,但不能使用子类特有的方法。子类的引用/指针也不能接收父类的对象。

何时使用引用参数
  • 大对象,值传递会创建一个完整的副本,如果参数是体积大的对象,对内存的开销很大。

  • 函数需要修改实参的值时,使用指针可读性低而且不够安全。

const修饰---------------------------------------------------------------------------------

  • 大对象,可以使用const指针或const引用。
  • 如果传递对象是类对象,使用const引用,类设计常常要求使用引用。
何时使用值传递
  • 传小对象。

  • 如果对象是数组,则使用指针,这是唯一选择,并将指针用const修饰(使用const修饰是为了防止函数内部意外修改数组的内容(保护实参数组)。

相关推荐
雨中飘荡的记忆5 小时前
Javassist实战
java
陈文锦丫5 小时前
微服务-----
java·数据库·微服务
任子菲阳5 小时前
学Java第五十三天——IO综合练习(1)
java·开发语言·爬虫
繁华似锦respect5 小时前
单例模式出现多个单例怎么确定初始化顺序?
java·开发语言·c++·单例模式·设计模式·哈希算法·散列表
Unstoppable225 小时前
八股训练营第 38 天 | 类加载机制介绍一下?介绍一下双亲委派机制?说一说你对 Spring AOP 的了解?说一说你对 Spring 中 IoC 的理解?
java·jvm·spring
帝吃藕和5 小时前
Java中数组、ArrayList和List的初始化与转换
java·list
风止何安啊5 小时前
递归 VS 动态规划:从 “无限套娃计算器” 到 “积木式解题神器”
前端·javascript·算法
ohyeah5 小时前
使用 Vue 3 实现大模型流式输出:从零搭建一个简易对话 Demo
前端·vue.js·openai
GPTMirrors镜像系统5 小时前
JS 实现指定 UA 访问网站跳转弹窗提醒,解决夸克等浏览器兼容性问题
前端·javascript