C++ Primer 递增和递减运算符

server/2025/2/8 11:17:14/

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 4.5递增和递减运算符
    • 在一条语句中混用解引用和递增运算符
    • 运算对象可按任意顺序求值

4.5递增和递减运算符

递增运算符(++)和递减运算符(一)为对象的加1和减1操作提供了一种简洁的书写形式。这两个运算符还可应用于迭代器,因为很多迭代器本身不支持算术运算,所以此时递增和递减运算符除了书写简洁外还是必须的。

递增和递减运算符有两种形式:前置版本和后置版本。到目前为止,本书使用的都是前置版本,这种形式的运算符首先将运算对象加1(或减1),然后将改变后的对象作为求值结果。后置版本也会将运算对象加1(或减1),但是求值结果是运算对象改变之前那个值的副本:

int i = 0;
j=++j;
j<i++;
  1. 前置版本得到递增之后的值
  2. 后置版本得到递增之前的值

这两种运算符必须作用于左值运算对象。前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。

建议:除非必须,否则不用递增递减运算符的后置版本

有C语言经验的读者可能对优先使用前置版本递增运算待有所疑问,其实原因非常简单:前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。

对于整数和指针类型来说,编译器可能对这种额外的工作进行一定的优化;但是对于相对复杂的迭代器类型,这种额外的工作就消耗巨大了。建议养成使用前置版本的习惯,这样不仅不需要担心性能的问题,而且更重要的是写出的代码会更征合编程的初衷。

在一条语句中混用解引用和递增运算符

如果我们想在一条复合表达式中既将变量加1或减1又能使用它原来的值,这时就可以使用递增和递减运算符的后置版本。

举个例子,可以使用后置的递增运算符来控制循环输出一个vector对象内容直至遇到(但不包括)第一个负值为止:

auto pbeg=v.begin();
// 输出元素直至遇到第一个负值为止
while(pbeg!*v.end()8&*beg>=0)cout<<*pbeg++<<endl;//输出当前值并将ppeg向前移动一个元素

对于刚接触C++和C的程序员来说,*pbeg++不太容易理解。其实这种写法非常普遍,所以程序员一定要理解其含义。

后置递增运算符的优先级高于解引用运算符,因此pbeg++等价于(pbeg++)。pbeg++把pbeg的值加1,然后返回pbeg的初始值的副本作为其求值结果,此时解引用运算符的运算对象是pbeg未增加之前的值。最终,这条语句输出pbeg开始时指向的那个元素,并将指针向前移动一个位置。

这种用法完全是基于一个事实,即后置递增运算符返回初始的未加1的值。如果返回的是加1之后的值,解引用该值将产生错误的结果。不但无法输出第一个元素,而且更糟糕的是如果序列中没有负值,程序将可能试图解引用一个根本不存在的元素。

建议:简洁可以成为一种美德

形如*pbeg++的表达式一开始可能不太容易理解,但其实这是一种被广泛使用的、有效的写法。当对这种形式熟悉之后。

cout<<iter++<<endl;

要比书写下面的等价语旬更简洁、也更少出错

cout<<*iter<<endl;
++iter;

不断研究这样的例子直到对它们的意义一目了然。大多数C++程序追求简洁.摒弃冗长,因此C++程序员应该习惯于这种写法。而且,一旦熟练掌握了这种写法后,程序出错的可能性也会降低。

运算对象可按任意顺序求值

大多数运算符都没有规定运算对象的求值顺序,这在一般情况下不会有什么影响。然而,如果一条子表达式改变了某个运算对象的值,另一条子表达式叉要使用该值的话,运算对象的求值顺序就很关键了。因为递增运算符和递减运算符会改变运算对象的值,所以要提防在复合表达式中错用这两个运算符。为了说明这一问题,该程序使用for循环将输入的第一个单词改成大写形式:

for(auto it=s.begin();it!s.end()&&!isspace(*it);++it)
*it->loupper(*it);//将当前字符改成大写形式
// 在上述程序中,我们把解引用it和递增it两项任务分开来完成。如果用一个看似等价的while循环进行代替
//该循环的行为是未定义的!
while(beg!=s.end()&&!isspace(*beg))
*beg=toupper(*beg++);//错误:该赋值语句未定义

将产生未定义的行为。问题在于:赋值运算符左右两端的运算对象都用到了beg,并且右侧的运算对象还改变了beg的值,所以该赋值语句是未定义的。编译器可能按照下面的任意一种思路处理该表达式:

*beg=toupper(*beg);//如果先求左侧的值
*(beg+1)=toupper(*beg);//如果先求右侧的值

http://www.ppmy.cn/server/165933.html

相关文章

启明星辰发布MAF大模型应用防火墙产品,提升DeepSeek类企业用户安全

2月7日&#xff0c;启明星辰面向DeepSeek等企业级大模型业务服务者提供的安全防护产品——天清MAF&#xff08;Model Application Firewall&#xff09;大模型应用防火墙产品正式发布。 一个新赛道将被开启…… DeepSeek的低成本引爆赛道规模 随着DeepSeek成为当前最热的现象级…

【PyQt5 12】如何加载QT designer 设计的界面

一、动态加载UI文件 from PyQt5.QtWidgets import QApplication,QWidget from PyQt5 import uic import sys class MyWindow(QWidget):def __init__(self):# 从文件中加载UI定义super().__init__()self.ui uic.loadUi("my.ui")self.ui.button.clicked.connect(self…

async-http-client使用示例

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 async-http-client是一个用于 Java 平台的高性能、非阻塞 HTTP 客户端库&#xff0c;它允许开发者以异步的方式发送 HTTP 请求并处理响应&#xff0c;从而提高应用程序的性能和响应性。 主要特点 异步处理&#xff…

【零基础学Mysql】常用函数讲解,提升数据操作效率的利器

以耳倾听世间繁华&#xff0c;以语表达心中所想 大家好,我是whisperrrr. 前言&#xff1a; 大家好&#xff0c;我是你们的朋友whisrrr。在日常工作中&#xff0c;MySQL作为一款广泛使用的开源关系型数据库&#xff0c;其强大的功能为我们提供了便捷的数据存储和管理手段。而在…

【前端】【面试】【经典一道题】前端 Vue、React 采用单向数据流的原因

前端Vue、React采用单向数据流的原因 一、可预测性 1. 数据流向清晰 在单向数据流架构里&#xff0c;数据从父组件流向子组件的路径是明确且可预期的。 React示例&#xff1a;父组件通过 props 传递数据给子组件&#xff0c;子组件只能读取 props 中的数据&#xff0c;没有直…

快速对QWen2.5大模型进行微调

先看看训练结果&#xff1a; 目录 前言什么是LLaMA-Factory&#xff1f;安装LLaMA-Factory准备数据集配置微调参数运行微调脚本评估和保存模型使用微调后的模型可视化微调大模型总结 前言 在当今人工智能领域&#xff0c;大模型&#xff08;如LLaMA、GPT等&#xff09;的微调…

OpenCV4.8 开发实战系列专栏之 30 - OpenCV中的自定义滤波器

欢迎大家学习OpenCV4.8 开发实战专栏&#xff0c;长期更新&#xff0c;不断分享源码。 专栏代码全部基于C 与Python双语演示。 送相关学习资料, V&#xff1a; OpenCVXueTang_Asst 本文关键知识点&#xff1a;OpenCV中的自定义滤波器 图像卷积最主要功能有图像模糊、锐化、梯…

c++ 面试题

C 面试题通常涵盖基础知识、面向对象编程、内存管理、模板、STL&#xff08;标准模板库&#xff09;等方面。以下是一些常见的 C 面试题及其简要解答&#xff0c;供你参考&#xff1a; 1. C 基础知识 1.1 C 和 C 的区别是什么&#xff1f; C 是 C 的超集&#xff0c;支持面向…