C++ 的表达式要不然是右值 (rvalue), 要不然是左值 (lvalue). 这两个名词是从 C 语言继承过来的, 原本是为了帮助记忆: 左值可以位于赋值语句的左侧, 右值则不能.
当一个对象被用做右值的时候, 用的是对象的值 (内容); 当对象被用做左值的时候, 用的是对象的身份 (在内存中的位置).
前置版本++i
: 首先将运算对象加 1, 然后将改变后的对象作为求值结果.
后置版本i++
: 也会将运算对象加 1, 但是求解结果是运算对象改变之前的那个值的副本.
以下摘录自 More Effective C++ Item 6:
// prefix form(++i): increment and fetch
UPInt& UPInt::operator++()
{
*this +=1; // increment
return *this; // fetch
}
// postfix form(i++): fetch and increment
const UPInt UPInt::operator++(int)
{
const UpInt oldValue = *this; // fetch
++(*this); // increment
return oldValue; // return what was fetched
}
sizeof
运算符的结果部分地依赖于其作用的类型:
-
对 char 或者类型为 char 的表达式执行 sizeof 运算, 结果得 1.
-
对引用类型执行 sizeof 运算得到被引用对象所占空间的大小.
-
对指针执行 sizeof 运算得到指针本身所占空间的大小.
-
对解引用指针执行 sizeof 运算得到指针指向的对象所占空间的大小.
-
对数组执行 sizeof 运算得到整个数组所占空间的大小, 等价于对数组中所有元素各执行一次 sizeof 运算并将所得结果求和.
-
对 string 对象或 vector 对象执行 sizeof 运算只返回该类型固定部分的大小.
class A {};
class B { B(); ~B() {} };
class C { C(); virtual ~C() {} };
class D { D(); ~D() {} int d; };
class E { E(); ~E() {} static int e; };
int main(int argc, char* argv[]) {
std::cout << sizeof(A) << std::endl; // 输出结果为1
std::cout << sizeof(B) << std::endl; // 输出结果为1
std::cout << sizeof(C) << std::endl; // 输出结果为8,实例中有一个指向虚函数表的指针
std::cout << sizeof(D) << std::endl; // 输出结果为4,int占4个字节
std::cout << sizeof(E) << std::endl; // 输出结果为1,static不算
return 0;
}
-
定义一个空类型, 里面没有成员变量和成员函数, 求 sizeof 结果为 1. 空类型的实例中不包括任何信息, 本来求 sizeof 得到 0, 但是当我们声明该类型的实例的时候, 它必须在内存中占有一定的空间, 否则则无法使用这些实例, 至于占用多少内存, 由编译器决定, 一般有一个 char 类新的内存.
-
如果在该类型中添加一个构造函数和析构函数, 再对该类型求 sizeof 结果仍为 1. 调用构造函数和析构函数只需要知道函数的地址即可, 而这些函数的类型只与类型相关, 而与类型的实例无关, 编译器也不会因为这两个函数在实例内添加任何额外的信息.
-
如果把析构函数标记为虚函数, 就会为该类型生成虚函数表, 并在该类型的每一个实例中添加一个指向虚函数表的指针. 在 32 位的机器上, 一个指针占 4 字节的空间, 因此求 sizeof 得到 4; 在 64 位机器上, 一个指针占 8 字节的空间, 因此求 sizeof 得到 8.
-
static_cast: 任何具有明确定义的类型转换, 只要不包含底层 const, 都可以使用 static_cast.
-
dynamic_cast: 用于 (动态) 多态类型转换. 只能用于含有虚函数的类, 用于类层次间的向上向下转化.
-
const_cast: 去除 "指向常量的指针" 的 const 性质.
-
reinterpret_cast: 为运算对象的位模式提供较低层次的重新解释, 常用于函数指针的转换.