表达式 = 运算符 + 运算对象。字面值、变量是最简单的表达式,两个以上运算符的是复合表达式。
- 一元运算符
- &取地址符
- *解引用符
- 二元运算符
- ==相等运算符
- +-*/运算符
- 三元运算符
- 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 == 3
21 % 7 == 0; 21 / 7 == 3
-21 % -8 == -5; -21 / -8 == 2
21 % -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=3
k = {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 11111111
unsigned 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) == 1
sizeof(*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 | 左 |