- 如何选择算术类型?
当明确知晓数值不可能为负的时候,选用无符号类型;
不要使用char和bool,对于不大的整数,明确指定signed char或unsigned char;
使用int执行整数运算(而非short和long),太大的数值选用long long;
执行浮点运算选用double,因为float通常精度不够,且两者运算代价相差不大;
unsigned char c = -1; //假设char占8比特,c值为255; signed char c2 = 256; //假设char占8比特,c2的值是未定义的
说明:当赋给无符号类型一个超过它范围的值,结果是取模后的余数;如果赋给带符号类型一个超过它范围的值,结果是未定义的。
- 含有无符号类型的表达式
当一个算术表达式中既有无符号数又有int值,那么int会转换为无符号数;
两个无符号数进行算术运算,结果还是无符号数;
所以:切勿混用带符号类型和无符号类型。
- 初始化 VS 赋值(1)
在C++语言里,初始化是一个异常复杂的问题,很多程序员对于用等号=来初始化变量的方式倍感困惑,这种方式容易让人认为初始化是赋值的一种。在C++中有时这种区别无关紧要,但是这个概念异常重要。
- 列表初始化
用花括号进行初始化。一个重要特点是:如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器报错。
double d = 3.14; int a{d}, b = {d}; //错误 int c(d), e = d; //正确 double f{c}; //错误
- 默认初始化
对于内置类型,默认初始化的值由它定义的位置决定。定义于任何函数体之外的变量被初始化为0。定义于函数体之内的内置类型变量将不被初始化,即变量未定义。
- 声明一个变量
extern int i; //声明i而非定义i int j; //声明并定义j extern int k = 3; //定义k
在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。
- 引用
一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是拷贝。引用即别名。同时,因为引用本身不是一个对象,所以不能定义引用的引用。
引用的类型要与绑定的对象严格匹配。除了两种例外情况:
1.派生类与基类
2.允许为常量引用绑定非常量的对象、字面值、甚至表达式。允许常量引用绑定可以转换成引用类型的任意表达式作为初始值。
int i = 42; const int &r1 = i; // 正确 const int &r2 = 42; //正确 const int &r3 = r1*2; //正确 int &r4 = r1*2; //错误
具体原因如下:
double dval = 3.14; const int &ri = dval; //等价于 const int temp = dval; //临时变量 const int &ri = temp; // 绑定到临时变量 //如果ri不是常量,我们希望通过更改ri来更改dval;但实际上只能更改temp。 //所以当ri不是常量时,上述表达式非法
- 指针
指针的类型必须与其所指对象的类型一致,除了两种例外:
1.派生类与基类;
2.允许令一个指向常量的指针指向一个非常量对象。(注意与引用中的情况的区别)
- constexpr
常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。(只有用常量表达式初始化的const对象才是常量表达式~~)
函数体内的变量一般来说并非存放在固定地址内,因此constexpr不能指向这样的变量。但是函数体之外的变量可以。
const int max = 20; //常量表达式 const int limit = max+1; //常量表达式 int staff_size = 27; //不是常量表达式 const int sz = get_size(); //不是常量表达式
C++11规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。
Best Practice:如果你认定变量是一个常量表达式,那就把它声明为constexpr类型。
const与constexpr的区别:
const int* p = nullptr; // p是一个指向整形常量的指针 constexpr int* q = nullptr; //q是一个指向整数的常量指针。
constexpr把它所以定义的对象置为了顶层const。
- auto
auto类型有时候和初始值类型并不完全一样。
//1. auto 会忽略掉引用 int i = 0, &r = i; auto a = r; // a为int
//2. 忽略掉顶层const const int ci = i, &cr = ci; auto b = ci; //b是int auto c = cr; //c是int auto d = &i; // d是整型指针 auto e = &ci; //e是指向整型常量的指针
那么如何解决这两个问题呢?直接指定。
int i = 0; const int ci = i; const auto f = ci;//f是const int auto &g = ci;//g是一个整型常量引用(ci的const属性仍然保留) auto &h = 42;//错误 const auto &j = 42;
设置一个类型为auto的引用时,初始值中的顶层常量属性仍然保留。
- decltype
如果decltype使用的表达式是一个变量,那decltype返回变量的准确类型。
如果使用的表达式不是一个变量,那么:如果表达式结果对象是一个左值,则返回引用。
int i = 42, *p = &i, &r = i; decltype(r + 0) b; //b是一个int decltype(*p) c = b; //c是int& decltype((i)) d = b;//d是int& decltype(i) e;//e是int