表达式 = 运算符 + 运算对象。字面值、变量是最简单的表达式,两个以上运算符的是复合表达式。
- 一元运算符
- &取地址符
- *解引用符
- 二元运算符
- ==相等运算符
- +-*/运算符
- 三元运算符
- a ? b : c
- 多元运算符
- ()函数调用运算符
运算符其实就是普通函数,也可以重载,也有参数类型转换。但是不能改变运算符的运算对象个数、优先级、结合律等。
// bool fuck = a == b;bool operator==(Type left, Type right) {return left.a == left.b;}
不同运算符对运算对象的左右值有要求,有些要求必须是左值。
// =(赋值运算符)// 左侧必须是左值,返回的结果也是左值。// 左值 = ( 左值,左/右值 ) {// ......// return 左值// }// &(取地址符)// 参数必须是左值,返回一个右值(指针)。// 右值 &( 左值 ) {// ......// return 左值// }//*(解引用运算符)、[](下标运算符)、*(迭代器解引用运算符)// 参数可以是左/右值,返回是左值。// 左值 *( 左/右值 ) {// ......// return 左值// }// --,++(递增/递减运算符)// 前置版本// 左值 --/++( 左值 ) {// ......// return 左值}// 引用类型 = decltype( 左值 ),如果参数是左值,返回引用类型// 类型 = decltype( 右值 ),如果参数是右值,返回类型。
一、算术运算符
| 运算符 | 功能 | 用法 | |
|---|---|---|---|
| + | 一元正号 | +expr | 优先级依次递减 |
| - | 一元负号 | -expr | |
| * | 乘法 | expr * expr | |
| / | 除法 | expr / expr | |
| % | 取余 | expr % expr | |
| + | 加法 | expr + expr | |
| - | 减法 | expr - expr |
右值 operator算术运算符( 算术类型的右值1, 算术类型右值2(可选) ) {// 右值1和右值2类型相同,不同会先进行类型转换,数值提升(小整数提升为较大整数)return 右值}副本 operator一元+号 ( 算数值/指针 ) {return 副本}副本 operator一元-号( 算数值 ) {return 副本}
// 如何计算取余?// 15 % 6 = ?// 15 % -6 = ?// -15 % 6 = ?// -15 % -6 = ?/************取余运算符%定义:************/// if( m是整 && n是整 && n != 0 )// (m / n) * n + m % n == m //这是取余的定义// 所以可以推出,取余公式:// m % n == m - (m / n)*n // 记住这个取余计算公式。/************取余运算符%定义:************/m % (-n) = m - (m /-n)*-n = m - (m/n)*n = m % n;(-m) % n == -m - (-m / n)*n = -(m - (m/n)*n) = -(m % n);21 % 6 == 3; 21 / 6 == 321 % 7 == 0; 21 / 7 == 3-21 % -8 == -5; -21 / -8 == 221 % -5 == 1; 21 / -5 == -4
二、逻辑、关系运算符
| 比较 | 运算对象 | 返回值 |
|---|---|---|
| 关系运算符 | 算术类型、指针 | bool |
| 逻辑运算符 | 能转换成bool的表达式 | bool |

bool右值 operator关系运算符( 算术/指针右值 ) {return bool右值}bool右值 operator逻辑运算符(可转成bool的类型右值){return bool右值}a && b // a为false,则不求值b,短路求值a || b // a为true,则不求值b,短路求值if (i < j < k) // i < j的布尔结果和 k 比较, 若 k 大于 1 则为真!if(i < j && j < k); // 优先级:算术 > 关系 > 逻辑;一元 > 二元 > 三元if((i < j) && (j < k)); // 与上等价if(val); // val非0成立。if(!val); // val为0成立if(val == true); // val == 1成立if(val == 1); // 与上等价。
三、赋值运算符=
a operator(可修改的左值a,可转成右侧对象类型值b){// b与a类型不同,则b将转成类型a。return a;}int i = 0; j = 0; k = 0; // 初始化非赋值const int ci = i; // 初始化非赋值1024 = k; // 错误:=左侧值必须是左值,1024是右值(i+j) = k; // 错误;+返回右值,=左侧要求左值。ci = k; // 错误,ci是特殊类型左值,也算是右值,不能出现在=左侧。k = 0;k = 3.14159; // k=3k = {3.14}; // 错误,窄化转换,这是{}括号的检测功能,多多使用。vector<int> vi;vi = {0, 1, 2, 3, 4, 5, 6}; // vector重载了=运算符。ival = jval = 0; // 右结合定律,int ival , *pval ;ival = pval = 0 ; // 错误:pval = 0的返回值是int*类型。/***********复合运算符***********/+= -= *= /= %= // 算术运算<<= >>= &= ^= |= // 位运算
四、递增++、递减—运算符
// 前缀递增/递减,++a, --b// 返回对象本身T& T::operator++() {*this += 1;return *this;}// 后缀递增/递减,a--, b++// 返回对象的副本T T::operator++(int){T ret = *this;*this += 1;return ret;}
int i, *p = 1, &i;*p++; // 等价于*(p++),递增 > *(解引用)*p + 1; // 等价于(*p)+1,一元(*) > 二元(+)cout << *iter++ << endl;//简洁为美,且少错。多使用上面,代替下面,在C++中很常见。cout << *iter << endl; ++iter;
五、成员访问运算符
T T::operator.( ){ // 点运算符}左值 T::operator->( T* ){ // ->运算符return 左值;}
六、条件运算符
auto fuck = cond ? expr1 : expr2;// 当cond为true时,对expr1求值并返回。// 当cond为false时,对expr2求值并返回。// 当expr1、expr2都是左值或可以转换成左值时,返回左值,否则返回右值。
七、位运算符
| 运算符 | 功能 | 用法 |
|---|---|---|
| ~ | 位求反 | ~expr |
| << | 左移 | expr1 << expr2 |
| >> | 右移 | expr1 >> expr2 |
| & | 位与 | expr & expr |
| ^ | 位异或 | expr ^ expr |
| | | 位或 | expr | expr |
移位运算符( <<、>>)也叫IO运算符。
int i = 1; //char c = 2; //int i1 = -1; // 有符号负数。c >> 1; // 小整型会提升到大整型:char会提升到int。auto k = i & c; // c会数值提升成int类型,k的类型就变成了int。auto f = i1 >> 1; // 行为未定义,i1是有符号位,右移结果依赖于机器。auto h = ((unsigned int) i1) >> 1; // 2^32 - 1,h的类型是unsigned int// 无符号的右移最左边补齐0,因此位运算符的运算对象最好是无符号。// 无符号i1的二进制: 11111111 11111111 11111111 11111110// i1 >> 1: 01111111 11111111 11111111 11111111unsigned char bits = 0227; // 10010111// bits会数值提升成int~bits; // 11111111 11111111 11111111 01101000
八、sizeof运算符
返回sizeof(…),括号里面…所代表的数据类型占用的字节数。在编译阶段完成,因此如果…是表达式,不会计算其结果,更不会检查表达式的逻辑错误(空指针,未初始化)。
// 括号里面可以是type某种数据类型(内置类型、类类型)、任意表达式size_t num = sizeof(type) // 类型的字节数。size_t num = sizeof(expr) // 返回表达式结果类型的字节数,不计算。sizeof(char) == 1sizeof(*p) == 指针对象的类型大小sizeof(string) == 空string的大小sizeof(vector) == 空vector的大小sizeof(引用) == 引用对象的大小sizeof(指针) == 指针值的大小(4字节、8字节)sizeof(数组) == 数组大小A a; // 类型A可以是任意类型A& b = a; // 引用A* c = &a; // 指针A array[10]; // 数组int size = sizeof(A) // A类型的字节数int size = sizeof(a); // A类型的字节数int size = sizeof(b); // A类型的字节数int size = sizeof(c); // 一个指针的字节数,4/8个字节,平台各异。int size = sizeof(array); // 数组的大小,10 * sizeof(A)void fuck(A a[], int length){int size = sizeof(a); // 这是指针大小,数组是转成了指针进行参数传递。}Sales_data data , *p;sizeof(Sales_data); // 存储 Sales_data 类型的对象所占的空间大小sizeof(data); // 与上等价sizeof(p); // 指针所占的空间大小sizeof(*p); // 等价于sizeof(data)sizeof(data.revenue); // Sales_data 的 revenue 成员对应类型 的大小sizeof(Sales_data::revenue); // 成员revenue的类型的大小
计算类大小
sizeof(类)是计算类对象的大小,考虑因子如下:
- 内存对齐填充的字节数
- 非静态成员变量,静态成员变量在全局区,并不在对象内存中。
- 是否为有虚函数,有虚函数则所有类对象都多了一个虚函数表指针。
- 是否是虚继承
- 空类大小为1个字节 ```cpp
// 假设在64位编译器中编译,对齐基数为8字节
// A的成员变量首地址必须被这个数整除,min(对齐基数,A中最宽基本类型大小) = 4字节。 class A { private: static int s_var; // 不影响类对象大小 const int c_var; // 4字节 int var; // 4字节(上) + 4字节(var) = 8字节 char var1; // 8字节(上)+ 1字节(var1)+ 3字节(填充)= 12字节。 public: A(int temp) : c_var(temp){} // 成员函数,不影响 ~A(){} // 成员函数,不影响
virtual void f() { cout << "A::f" << endl; } // 有虚函数,则每个类对象都会有一个虚函数表指针virtual void g() { cout << "A::g" << endl; } // 多个虚函数,也都是只添加一个虚函数表指针virtual void h() { cout << "A::h" << endl; }
}
int main(){ A a(4); A *p;
cout << sizeof(p) << endl; // 8字节,指针大小。cout << sizeof(ex1) << endl; // 24字节return 0;
}
<a name="SWnsU"></a>## 与strlen区别strlen是c库函数,在<cstring>中声明。返回字符串的长度(以\0结尾,不包含\0字符)。在运行时计算长度。<br />```cpp// strlen源代码size_t strlen(const char* str){size_t size_ret = 0;while(*str++) ++size_ret;return size_ret;}
九、逗号运算符
// expr1, expr2,这就是执行了,号运算符// expr1左侧表达式,expr2右侧表达式expr2结果 operator,(expr1, expr2) {1、对expr1表达式求值,然后丢弃2、对expr2表达式求值,并返回。}
十、运算符优先级
算术 > 关系 > 逻辑。
一元 > 二元 > 三元。
| 上往下,优先级依次降低 | |||
|---|---|---|---|
| 运算符 | 功能 | 用法 | 结合律 |
| :: | 全局作用域 | ::name | 左 |
| :: | 类作用域 | class::name | 左 |
| :: | 命名空间作用域 | namespace::name | 左 |
| . | 成员选择 | a.mem | 左 |
| -> | 成员选择 | p->mem | 左 |
| [] | 下标 | expr[expr] | 左 |
| () | 函数调用 | func(params) | 左 |
| () | 类型构造 | type(expr_list) | 左 |
| ++ | 后置递增 | lvalue++ | 右 |
| — | 后置递减运算 | lvalue— | 右 |
| typeid | 类型ID | typeid(type) | 右 |
| typeid | 运行时类型ID | typeid(expr) | 右 |
| explicit cast | 类型转换 | cast_name |
右 |
| ++ | 前置递增 | ++lvalue | 右 |
| — | 前置递减 | —lvaule | 右 |
| ~ | 位求反 | ~expr | 右 |
| ! | 逻辑非 | !expr | 右 |
| - | 一元负号 | -expr | 右 |
| + | 一元正号 | +expr | 右 |
| * | 解引用 | *expr | 右 |
| & | 取地址 | &expr | 右 |
| () | 类型转换 | (type)expr | 右 |
| sizeof | 对象的大小 | sizeof expr | 右 |
| sizeof | 类型大小 | sizeof(type) | 右 |
| sizeof … | 参数包的大小 | sizeof…(name) | 右 |
| new | 创建对象 | new type | 右 |
| new[] | 创建数组 | new type[size] | 右 |
| delete | 释放对象 | delete expr | 右 |
| delete[] | 释放数组 | delete[] expr | 右 |
| noexcept | 能否抛出异常 | noexcept(expr) | 右 |
| ->* | 指向成员选择的指针 | ptr->*ptr_to_mem | 左 |
| .* | 指向成员选择的指针 | obj.*ptr_to_mem | 左 |
| * | 乘法 | expr * expr | 左 |
| / | 除法 | expr / expr | 左 |
| % | 取模 | expr % expr | 左 |
| + | 加法 | expr + expr | 左 |
| - | 减法 | expr - expr | 左 |
| << | 向左移位 | expr << expr | 左 |
| >> | 向右移位 | expr >> expr | 左 |
| < | 小于 | expr < expr | 左 |
| <= | 小于等于 | expr <= expr | 左 |
| > | 大于 | expr > expr | 左 |
| >= | 大于等于 | expr >= expr | 左 |
| == | 等于 | expr == expr | 左 |
| != | 不等于 | expr != expr | 左 |
| & | 位与 | expr & expr | 左 |
| ^ | 位异或 | expr ^ expr | 左 |
| | | 位或 | expr | expr | 左 |
| && | 逻辑与 | expr && expr | 左 |
| || | 逻辑或 | expr || expr | 左 |
| ? : | 条件 | expr ? expr : expr | 左 |
| = | 赋值 | lvalue = expr | 右 |
| *=, /= ,%=,+= -=,<<=,>>=,&=,|=,^= |
复合赋值 | lvaule += expr | 右 |
| throw | 抛出异常 | throw expr | 右 |
| , | 逗号 | expr, expr | 左 |
