2018-12-18 C++ 学习笔记

一、const

(一) 指向常量的指针或引用

这一类指针或引用既可以指向常量也可以指向非常量,只不过这类指针或引用自以为自己指向了常量,所以自觉不去改变所指对象的值。

(二) 顶层 const 和底层 const

顶层 const 表示指针本身是个常量,底层 const 表示指针所指向的对象是一个常量。

int i = 0;
int *const p1 = &i; // 不能改变 p1 的值,这是一个顶层 const

const int ci = 42; // 不能改变 ci 的值,这是一个顶层 const
const int *p2 = &ci; // 允许改变 p2 的值,这是一个底层 const

const int *const p3 = p2; // 靠右的 const 是顶层 const,靠左的是底层 const
const int &r = ci; // 用于声明引用的 const 都是底层 const

执行拷贝操作时,因为被拷贝对象的值不会被改变,所以拷贝者与被拷贝者是否为常量都没什么关系。

故顶层 const 在执行拷贝操作时不受什么影响,而底层 const 却有所限制。当执行对象的拷贝操作时,拷贝者和被拷贝者必须具有相同的底层 const 资格,或者两个对象的数据类型必须能够转换。

int *p = p3; // 错误,p 没有跟 p3 一样的底层 const
p2 = p3; // 正确,都是底层const
p2 = &i; // 正确,int* 可以转换成 const int*
const int &r2 = i; // 正确,const int& 可以绑定到普通 int 上

二、constexpr 和常量表达式

常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。

(一) constexpr 变量

将变量声明为 constexpr 可以让编译器来验证变量的值是否为一个常量表达式。

constexpr 只能修饰字面值类型(算术类型,指针,引用)变量。

(二) 指针与 constexpr

const int *p = nullptr; // p 是一个指向整形常量的指针
constexpr int *q = nullptr; // q 是一个指向普通整形变量的常量指针

constexpr int i = 42; // i 必须定义在函数体之外
constexpr const int *ccp = &i; // ccp是常量指针,指向整形常量 i

(三) constexpr 函数

遵循两项约定:

  • 函数的返回类型和所有形参的类型都得是字面值类型。
  • 函数体中有且只有一条 return 语句。

通常定义在头文件。

扫描二维码关注公众号,回复: 4636030 查看本文章

三、auto

auto 推断出来的类型一般会忽略顶层 const 而保留底层 const,如需要,需手动添加 const

const int ci = 30, &cr = ci;

auto b = ci; // b 是 int,不是 const int
auto c = cr; // c 是 int,因为 cr 是 ci 的别名,ci 是 const int
auto d = &ci; // d 是 const int*,因为底层 const 被保留

// 以下在 auto 前面添加的 const 为顶层 const
const auto e = ci; // e 是 const int
const auto f = &ci; // f 是 const int* const
auto &g = ci; // g 为 const int&
auto &h = 42; // auto 推断结果为 int,h 为 int&,不能绑定字面值,需要在 auto 前面添加 const
auto *p = &ci; // p 为 const int*,同 d

总结:

  • auto 不会让其修饰的变量成为一个常量,但可以成为一个指向常量的指针。
  • auto 在推断时会保留右值的底层 const 属性。
  • 若右值是引用类型,则 auto 会把引用单纯看成别名,追溯该引用所绑定的变量的类型,并以该类型为推导依据。
  • auto 后面紧跟着符号 & 或 *,则 auto 会结合它们进行推断。

四、decltype 类型指示符

有时会想要从表达式的类型推断出要定义的变量的类型,但是不想用该表达式的值初始化变量。decltype 可选择并返回操作数的数据类型。

表达式为变量时:

const int ci = 0, &cj = ci;
decltype(ci) x = 0; // 顶层 const 被保留,x 是 const int
decltype(cj) y = x; // 引用被保留,cj 是 const int&,故 y 也一样
decltype(cj) z; // 错误,z 是一个引用类型,必须初始化

int j = 30;
decltype((j)) m; // 错误,m 是 int&,必须初始化
decltype(j) n; // n 是 int

表达式不是变量时:

int i = 42;
int *p = &i;
int &r = i;

decltype(r + 10) b; // 加法的结果为 int,故 b 是 int,只不过未初始化
decltype(*p) c; // 错误,c 是 int&,必须初始化 

总结:

  • 若表达式为变量且没有加括号,则 decltype 将原封不动地返回该变量的原本类型,既不忽略顶层 const ,也不会把引用单纯看成别名。
  • 若表达式为加了括号的变量,则返回的类型为该变量类型的引用。
  • 若表达式不是变量且不是解引用操作,则返回的类型为表达式运算结果对应的类型。
  • 若表达式为变量且为解引用操作,则返回类型为引用。

五、string 类新知

(一) 使用范围 for 语句改变字符串中的字符

string s("HUANGHAOBO");
for(auto &c : s){
    c = toupper(c); // c 是一个引用,该行语句会改变 s 中字符的值
}

(二) 下标类型 decltype(s.size()) 和 string::size_type

for(decltype(s.size()) i = 0; i < s.size(); i++){ /*...*/ }

string::size_type n;
while(cin >> n){
    if(n < s.size()){ /*...*/ }
}

六、vector 模板类新知

(一) 列表初始化

vector<string> v1{"aa", "bb", "cc"}; // 列表初始化
vector<string> v2("aa", "bb", "cc"); // 错误

vector<int> v3(10, 1); // 有 10 个元素,元素值皆为 1
vector<int> v3{10, 1}; // 列表初始化,有 2 个元素,值分别是 10 和 1

七、参考资料

《C++ Primer(第五版)》

猜你喜欢

转载自blog.csdn.net/weixin_36725931/article/details/85218076