C++ 学习笔记(4)表达式、运算符、类型转换(static_cast、const_cast、reinterpret_cast、dynamic_cast)

C++ 学习笔记(4)表达式、运算符、类型转换(static_cast、const_cast、reinterpret_cast、dynamic_cast)

参考书籍:《C++ Primer 5th》


4.1 基础

4.1.1 基本概念

  • 左值:对象的身份(在内存所在的位置)。
    • 赋值对象。
    • 取地址符作用对象是左值,返回结果指针是右值。
    • 内置解引用运算符、下表运算符对象。
    • 内置类型和迭代器的递增递减运算符对象。
  • 右值:对象的值(内容)。
  • decltype作用于左值时,得到引用类型。
int a = 0;
int *p = &a;
decltype(*p) b;		// int &b 。解引用生成左值,引用类型(未初始化)。
decltype(&p) c;		// int **c 。取地址生成右值,指针的指针。

4.1.3 求值顺序

  • 优先级规定了运算对象的组合方式,但是运算对象之间没有明确的求值顺序。如果表达式(函数)指向并修改了同一对象,将产生未定义的行为。
int a = f1() * f2();					// 无法判断 f1 和 f2 的调用顺序

int i = 0;
cout << i << " "<< ++i << endl;		// 未定义:可能输出 1 1

4.2 算术运算符

  • 求余:m % n 如果不等于0,其结果的符号和m相同(与n无关)。

4.4 赋值运算符

  • 对于复合运算符(+=、-=等)都等价于(a = a op b)。区别在于复合运算符只求一次值,普通的运算符要求值两次(一次计算、一次赋值)。

4.5 递增和递减运算符

  • 前置版本(++a):对象本身作为左值返回。
  • 后置版本(a++):对象原始值的副本作为右值返回。
// iter的类型是vector<string>::iterator

*iter++;  		// 正确。返回 *iter,然后++iter.
(*iter)++;  		// 错误。*iter 字符串没有递增操作。
*iter.empty() 	// 错误。'.'运算符优先级高于'*'。
iter->empty();  	// 正确。
++*iter;        		// 错误。*iter 字符串没有递增操作。
iter++->empty();  	// 正确。执行函数后自加。

4.8 位运算符

  • 左移:右侧加入0。
  • 右移:
    • 无符号类型:左侧加入0。
    • 有符号类型:左侧加入符号的副本或0,如何选择视具体环境而定。

4.9 sizeof运算符

  • 两种形式,一种是类型,一种是表达式:
    • sizeof( type )
    • sizeof expr
  • 在sizeof的运算对象中解引用一个无效指针仍然是安全的,sizeof不需要真的解引用指针也能知道他所指对象的类型。
  • sizeof运算符的结果部分地依赖于其作用的类型:
    • char:1。
    • 引用类型:被引用对象所占空间的大小。
    • 指针:指针本身所占空间大小。
    • 指针解引用:对象所占空间大小。
    • 数组:整个数组所占空间的大小。
    • string对象或vector对象:返回类型固定部分大小。(不计算元素占用多少空间)

4.11 类型转换

  • 隐式转换(implicit conversion)
    • 在对大多数表达式中,比int小的整形(包括bool、char)首先提升为较大的整数类型。
    • 在条件中,非布尔类型转换成布尔类型(非0 -> true、 0 -> false)。
    • 初始化值转换成变量的类型;赋值时,右侧对象转换成左侧运算对象类型。
    • 算术运算或关系运算的运算对象,需要转换成同一种类型。

4.11.1 算术转换

  • 运算对象一个是无符号类型、另一个是有符号类型时:
    • 无符号 ≥ 有符号:带符号的运算对象转换成无符号。如果有符号的值为负,结果无法判断。
    • 无符号 < 有符号:转换结果依赖与机器。
      • 无符号的值能存在有符号类型中,无符号类型转换成有符号类型。
      • 无符号的值不能存在有符号类型中,有符号类型转换成无符号类型。

4.11.3 显示转换

static_cast

任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。

  • 在较大算术类型赋给较小类型时,相当于明确告诉编译器:“我知道且不在乎精度损失”,编译器就不会出现相关警告提示。
  • 可用于无法自动执行类型转换的对象。如找回void*指针的值:
double d;
void* p = &d;
double *dp = static_cast<double*>(p);	// 将void*转回原来的指针类型。如果类型不符,会产生未定义后果。
const_cast

**只能改变运算对象的底层const,通常用于将常量对象转换成非常量。只能用于指针或引用。**常用于函数重载的上下文中,更多:C++标准转换运算符const_cast

  • 如果对象本身不是一个常量,这种强制转换是合法的。
  • 如果对象本身是一个常量,这种强制转换会产生未定义的后果。
  • 不能改变表达式类型。
reinterpret_cast

常用于运算对象的位模式,提供较低层次上的重新解释。本质上依赖于机器。使用这个是非常危险的,如下:pc指针所指的对象实际是一个int而非字符。

int *ip;
char *pc = reinterpret_cast<char*>(ip);
// 等价 char *pc = (char*) ip;

string str(pc);		// 运行时错误。pc实质指的是一个int。
dynamic_cast (19.2.1)

将一个基类对象指针(或引用)cast到继承类指针(或引用)。更多:理解C++ dynamic_cast - Todd Wei - 博客园

  • 有三种形式(type为类类型,通常含有虚函数):
    • dynamic_cast<type*>(e) :e必须为有效的指针。
    • dynamic_cast<type&>(e) :e必须为一个左值。
    • dynamic_cast<type&&>(e) :e不能是左值。
  • e的类型必须符合以下三个条件中任意一个:
    • 是目标type的公有派生类。
    • 是目标type的公有基类。
    • 是目标type的类型。
  • 转换失败时:
    • 目标是指针类型,返回0。
    • 目标是引用类型,抛出bad_cast异常。
// 指针类型
// bp指针指向基类Base(至少含有一个虚函数),Derived是Base的公有派生类。
if (Derived *dp = dynamic_cast<Derived*>(bp))
{
	// 转换成功。dp指向Derived对象。
} 
else 
{
	// 转换失败。使用dp指向的Base对象
}

// ----------------------------------------------------------------------------- //

// 引用类型
// 因为不存在空引用,对于引用失败,应该用捕获异常的方法。
void f(const Base &b)
{
	try {
		// 使用b引用的Derived对象。
		const Derived *d = dynamic_cast<Derived&>(b);
	} catch (bad_cast) {
		// 转换失败处理。
	}
}


运算符优先级表

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值