4-1 C++运算符基本概念
4.1.1 基本概念
函数观点
操作符可视为一个函数,用参数,副作用,返回值来分析它
如cout<<i<<j<<endl中>>操作符
- 参数:ostream对象和值(value)
- 副作用:将值输入到ostream中,这里是输入到标准输出(屏幕)中
- 返回值:返回左边的ostream对象,继续与右边的值结合,这是cout可以链式使用的原因
左值和右值
操作符的返回值可以分为左值(lvalue)和右值(rvalue)
- 左值:用的是对象的身份(内存中的位置)
- 右值:用的是对象的值(内容)
- 基本原则:需要右值时可以用左值代替,反之不可以。
运算符重载
对指定的类重新定义某些操作符的操作,但无法改变其运算对象的个数,优先级和结合律
4.1.2 优先级、结合律与求值顺序
优先级和结合律
- 优先级:每个操作符都有优先级,高级的先运算
- 结合律:同级运算符遵循结合律,从左到右运算
- 可用小括号强制改变运算顺序
求值顺序
书中表述
《C++ Primer(英)》137,对应中文版123:
优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值。在大多数情况下,不会明确指定求值的顺序。对于如下的表达式
int i = f1()* f2();
我们知道f1和f2一定会在执行乘法之前被调用,因为毕竟相乘的是这两个函数的返回值。但是我们无法知道到底f1在f2之前调用还是f2在f1之前调用。
因此,当在一个表达式的某处改变了一个变量的值,而在该表达式的另一处又使用到它,可能会产生一个未定义的结果,这是一个隐藏错误。

实践表明(猜想)
但是,在g++ 8.1版本下,编译器可以处理上述情况,输出0 1。
个人推测:编译器在执行cout<<i<<" "<<++i<<endl;时,会先从左到右执行指令,直到把所有子表达式为字面量。再从左到右输出,所以可以认为编译器执行了两步:(i=0为例)
- 子表达式
i和++i从左到右转为字面量:cout<<0<<" "<<1<<endl; - 输出
0 1
实践验证
#include<iostream>
using namespace std;
int m = 0; //全局变量,f,g,j都可以改变m的值
//三个函数都改变了m的值
int f(); //m++
int g(); //m+=2
int j(); //m*=2
int main(){
//如果f,g,j求值顺序不定的话,该式的输出结果会不同,结果未定义。
//但实际上程序输出19
cout<<f()+g()*j()<<endl;
return 0;
}
int f(){
m++;
return m;
}
int g(){
m+=2;
return m;
}
int j(){
m*=2;
return m;
}
按照上述猜想可以解释19这一结果
- 将子表达式
f、g、j从左到右执行,子表达式转为字面量- 执行
f后,m = 1 - 执行
g后,m = 3 - 执行
j后,m = 6 - 表达式变为
cout<<1 + 2*3<<endl;
- 执行
- 输出
19
可能的解释:编译器的优化行为
《C++ Primer(英)》138对应中文版124
所以或许可以认为,在遇到某一表达式的子表达式操作了同一对象且改变了对象的值时,编译器会遵循结合律,对每个字表达式从左到右求值,这一动作是编译器的优化动作,来提高执行效率。
但是否每种编译器都执行此类优化呢?不知道。
所以,如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。
一些运算符指定了求值顺序
一般运算符不指定求值顺序,但编译器会对其进行优化。而有些运算符明确指定了求值顺序,前三者都是从左到右。
&&:左边的子表达式为真时,才会继续求右边的子表达式的值||:左边的子表达式为假时,才会继续求右边的子表达式的值,(逗号)?:(三目条件判断运算符)
再述求值顺序
如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象,来避免编译器未定义行为
//编写程序把字符串的所有字符大写
#include<iostream>
#include<string>
using namespace std;
int main(){
string s = "hello, world";
auto it = s.begin();
while(it != s.end()){
*it = toupper(*it++); //右值改变了it,左值又用到了it,该行为结果是未定义的
}
cout<<s<<endl;
return 0;
}
*it = toupper(*it++);可能被解释为
*it = toupper(*it);*(it+1) = toupper(*it);

修改后:
#include<iostream>
#include<string>
using namespace std;
int main(){
string s = "hello, world";
auto it = s.begin();
while(it != s.end()){
*it = toupper(*it); //把两个语句分开,避免被修改的对象被重用
it++;
}
cout<<s<<endl;
return 0;
}

4-1 C++运算符基本概念的更多相关文章
- c/c++ 重载运算符 基本概念
重载运算符 基本概念 问题:对于int,float可以进行算数运算,但是对于一个自定义的类的对象进行算术运算,就不知道具体怎么运算了. 所以有了自定义运算符的概念. 1,自定义运算符其实就是一个以op ...
- C# 中的 null 包容运算符 “!” —— 概念、由来、用法和注意事项
在 2020 年的最后一天,博客园发起了一个开源项目:基于 .NET 的博客引擎 fluss,我抽空把源码下载下来看了下,发现在属性的定义中,有很多地方都用到了 null!,如下图所示: 这是什么用法 ...
- python类可以截获Python运算符
类可以截获Python运算符 现在,让我们来看类和模块的第三个主要差别: 运算符重载.简而言之,运算符重载就是让用类写成的对象,可截获并响应用在内置类型上的运算:加法.切片.打印和点号运算等.这只是自 ...
- C++学习(12)—— 运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型 1.加号运算符重载 作用:实现两个自定义数据类型相加的运算 #include <iostream> #i ...
- ...扩展运算符+rest参数+call/apply/bind
之前在set,map里面有提过扩展运算符的概念,但是今天偶然遇到一个问题,类似于扩展运算符的经典用法,突然发现对其了解不是很深,所以再来整理一下扩展运算符的相关知识. 重点:扩展运算符内部调用的是数据 ...
- C 语言运算符与分支循环小记
1. 前导内容 · 使用sizeof()统计各种数据类型占用字节数 · 进制转换问题 · if语句基本语法 · switch语句基本语法 2. 运算符 · 基本概念 · 左值 右值 · 优先级结合性 ...
- JS006. 详解自执行函数原理与数据类型的快速转换 (声明语句、表达式、运算符剖析)
今天的主角: Operator Description 一元正值符 " + "(MDN) 一元运算符, 如果操作数在之前不是number,试图将其转换为number. 圆括号运算符 ...
- 一篇文章带你了解Java中的运算符
前言 在前一篇文章中,壹哥给大家讲解了Java数据类型之间的转换,包括自动类型转换.强制类型转换.隐含的强制类型转换等问题.且在上一篇文章中,我还简单地给大家提到了Java的类型提升.在类型提升的案例 ...
- JavaSE自学笔记
ch03 [Thu Aug 18 2016 11:22:26 GMT+0800] 对象变量与对象之间是指代关系,对象变量并不能完全说明有无对象可用.这种指代关系是通过赋值运算建立起来的.对象变量保存的 ...
- Python2.6-原理之类和oop(上)
来自<python学习手册第四版>第六部分 一.oop:宏伟蓝图(26章) 在这之前的部分中,经常会使用"对象"这个词,其实,到目前为止都是以对象为基础的,在脚本中传递 ...
随机推荐
- MySQL编译安装-麒麟V10 x86
环境信息 操作系统: Kylin Linux Advanced Server V10 (Sword) 架构:X86 MySQL版本:5.7.44 编译 安装必要的依赖库和编译工具 sudo yum g ...
- 用GDI+旋转多边形来绘制一个时钟摸拟小程序
效果图 在头文件类中声明变量 TCHAR m_dayStr[4]; // 日期 TCHAR m_weekStr[4]; // 星期 Gdiplus::Font* m_pFont; // 字体 Gdip ...
- Win32 sdk 下树形控件响应鼠标单击与双击,获得选中项的名称
//窗口过程函数INT_PTR CALLBACK myWin::myWinDlgProc(HWND dlgHwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ...
- 使用 nuxi clean 命令清理 Nuxt 项目
title: 使用 nuxi clean 命令清理 Nuxt 项目 date: 2024/9/1 updated: 2024/9/1 author: cmdragon excerpt: nuxi cl ...
- C#/.NET/.NET Core优质学习资料,干货收藏!
前言 今天大姚给大家分享一些C#/.NET/.NET Core优质学习资料,希望可以帮助到有需要的小伙伴. 什么是 .NET? .NET 是一个免费的.跨平台的.开源开发人员平台,用于构建许多不同类型 ...
- opencv colors
""" 在利用python进行画图时,我们可能常常用的颜色就是'k'黑色,'r'红色,'b'蓝色,'g'绿色等,这些颜色分别代表常见的 几种颜色.但是当我们画图比较多时, ...
- ASP.NET Core Library – ImageSharp
前言 2021 年就写过一篇了, Asp.net core 学习笔记 Image processing (ImageSharp), 只是那时还是旧的写法, 这篇作为翻新和以后继续增加新功能的介绍. I ...
- Electron加载服务器端JS:高效打造跨平台桌面应用与本地 API 交互
在现代桌面应用开发中,使用 Electron 加载远程服务器托管的前端资源,再与本地 API 交互,能够带来灵活的部署和强大的本地功能支持.这种方式不仅提升了开发效率,还能充分利用 PC 端的资源和性 ...
- QT疑难杂症之如何使用自定义模型实现文件系统模型?类似QFileSystemModel,却比QFileSystemModel更好用
简介 本文讨论了QT文件系统模型QFileSystemModel的不足之处,并且讨论了改进目标,如何实现自定义文件系统模型,以及进一步改进的空间. 目录 QFileSystemModel的不足之处 改 ...
- 神经网络之卷积篇:详解为什么使用卷积?(Why convolutions?)
详解为什么使用卷积? 来分析一下卷积在神经网络中如此受用的原因,然后对如何整合这些卷积,如何通过一个标注过的训练集训练卷积神经网络做个简单概括.和只用全连接层相比,卷积层的两个主要优势在于参数共享和稀 ...
