本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
cs:programming:cpp:courses:cpp_basic_deep:chpt_2 [2024/04/16 03:57] – [常量与常量表达式] codinghare | cs:programming:cpp:courses:cpp_basic_deep:chpt_2 [2024/08/11 03:50] (当前版本) – [常量表达式指针] codinghare | ||
---|---|---|---|
行 125: | 行 125: | ||
int main() | int main() | ||
{ | { | ||
- | std::cout << | + | std::cout << |
} | } | ||
</ | </ | ||
行 242: | 行 242: | ||
==指针的引用== | ==指针的引用== | ||
* 指针是对象,因此存在指针的引用: | * 指针是对象,因此存在指针的引用: | ||
- | * 引用不是对象,因此不存在引用的应用 | + | * 引用不是对象,因此不存在引用的引用 |
<code cpp> | <code cpp> | ||
int* &refPx = ptr; | int* &refPx = ptr; | ||
行 252: | 行 252: | ||
* 编译器可以优化:常量不需要每次都判断(读取)值,但变量需要 | * 编译器可以优化:常量不需要每次都判断(读取)值,但变量需要 | ||
===常量指针与顶层常量=== | ===常量指针与顶层常量=== | ||
+ | 指针与常量结合时,需要考虑哪个部分不能被修改: | ||
+ | * 指针本身 | ||
+ | * 指针指向的部分 | ||
+ | 如果限制的是指针本身,那么: | ||
+ | <code cpp> | ||
+ | // 指针本身无法改变 | ||
+ | int* const ptr = &x; | ||
+ | </ | ||
+ | 如果限制的是指针指向的内容:,那么: | ||
+ | <code cpp> | ||
+ | // | ||
+ | const int* ptr = &x; | ||
+ | </ | ||
+ | <WRAP center round box 100%> | ||
+ | '' | ||
+ | </ | ||
+ | ==顶层常量== | ||
+ | * 顶层常量限制的是常量本身无法被修改 | ||
+ | * 底层常量限制的是指向的内容不能修改 | ||
+ | ==底层常量的隐式转换== | ||
+ | * 读写 -> 只读是可以的 | ||
+ | <code cpp> | ||
+ | // int * to const int * | ||
+ | int x = 4; | ||
+ | // &x 由 int* 转化为了 const int*,该指针之前可以对 x 进行读写,但现在只能读取 x | ||
+ | const int *ptr = &x; | ||
+ | </ | ||
+ | * 只读 -> 可写是不行的 | ||
+ | <code cpp> | ||
+ | // const int * to int * | ||
+ | const int x = 4; | ||
+ | // error, &x 无法写 x,因此 ptr 也不能对 x 进行写操作 | ||
+ | // low-level const 是有传递性的,如果之前只读,那么之后也必须只读(不管通过什么样的途径,比如赋值,参数传递等等) | ||
+ | int* ptr = &x; | ||
+ | // good | ||
+ | const int* ptr = &x; | ||
+ | </ | ||
+ | ==常量引用== | ||
+ | * const int& | ||
+ | * 主要是用于高效的函数传递参数: | ||
+ | * 传递某个大对象,但又不想改变该对象,就用 const int& | ||
+ | * 传递某些无法拷贝的对象 | ||
+ | * 相比指针,引用不需要判断参数的有效性(空指针什么的) | ||
+ | * built-in 数据不需要使用引用,值传递更效率一些 | ||
+ | * **常量引用可以绑定字面值**: | ||
+ | * 常量引用通常作为函数的 parameter,当函数采用字面值作为函数参数的时候,需要保证引用可以绑定该字面值,才能完成调用。 | ||
+ | ===常量表达式=== | ||
+ | 不同的常量确定的时机不同。比如下面的例子: | ||
+ | <code cpp> | ||
+ | int y; | ||
+ | std::cin >> y; | ||
+ | // y1 是在运行期确定的(运行期常量) | ||
+ | const int y1 = y; | ||
+ | // y2 是在编译期确定的(编译期常量) | ||
+ | const int y2 = 3; | ||
+ | </ | ||
+ | 由于这样的问题,编译器在处理 '' | ||
+ | * '' | ||
+ | * '' | ||
+ | 也就是说,编译器常量是可以被编译器优化的。这也是常量表达式的由来:C++ 11 中通过 '' | ||
+ | <code cpp> | ||
+ | // y2 被显式的声明为编译期常量 | ||
+ | // y2 的类型是 const int | ||
+ | // constexpr 是一种带指导意义的限定符(// | ||
+ | constexpr int y2 = 3; | ||
+ | </ | ||
+ | ==常量表达式指针== | ||
+ | 指针也可以作为编译期常量。这种情况下,需要满足两个条件: | ||
+ | * 指针是常量 | ||
+ | * 指针指向的内容也是常量 | ||
+ | 只有满足这两个条件,指针才能作为编译期常量对编译器可见。也就是说,这种情况下的指针类型是 '' | ||
+ | <code cpp> | ||
+ | constexpr const int* ptr = nullptr; | ||
+ | //字符串 | ||
+ | constexpr const char* str = " | ||
+ | </ | ||
+ | <WRAP center round tip 100%> | ||
+ | 可以使用 std:: | ||
+ | </ | ||
+ | ====类型别名与自动推导==== | ||
+ | ===类型别名=== | ||
+ | 类型可以引入别名,便利使用(比如 '' | ||
+ | * typedef + type + alias | ||
+ | * using alias = type | ||
+ | <code cpp> | ||
+ | //myInt 是 int 类型的别名 | ||
+ | // | ||
+ | // | ||
+ | typedef char myCharArr[4]; | ||
+ | using myCharArr = char[4]; | ||
+ | </ | ||
+ | ==类型别名与指针和引用== | ||
+ | * 当使用别名定义指针时,别名代表了指针的整体。此时如果使用 '' | ||
+ | <code cpp> | ||
+ | using intP = int*; | ||
+ | int x = 3; | ||
+ | |||
+ | // | ||
+ | const intP constIntPAlias = &x; | ||
+ | //等同于 top-const pointer | ||
+ | int* const constIntP = &x; | ||
+ | </ | ||
+ | * 不能使用别名来创建引用的引用 | ||
+ | ===类型推导=== | ||
+ | C++11 允许使用 '' | ||
+ | <code cpp> | ||
+ | auto x = 3.5 + 15l; | ||
+ | </ | ||
+ | ==auto== | ||
+ | * '' | ||
+ | * 因为需要初始化表达式推导类型,因此自动推导下,表达式**必须初始化** | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | int x1 = 3; | ||
+ | int& ref = x1; | ||
+ | // | ||
+ | int y = ref; | ||
+ | //auto 导致类型退化, | ||
+ | auto ref2 = ref; | ||
+ | </ | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | //const int | ||
+ | const auto x1 = 3; | ||
+ | //const int& | ||
+ | const auto& x2 = 3; | ||
+ | |||
+ | const int x3 = 3; | ||
+ | //const int | ||
+ | const auto y1 = x3; | ||
+ | //int (top-const 的退化) | ||
+ | auto z = x3; | ||
+ | </ | ||
+ | * auto 加引用会避免退化 | ||
+ | <code cpp> | ||
+ | // | ||
+ | auto& y2 = x3; | ||
+ | </ | ||
+ | * 数组的推导会退化为指向数组首元素的指针 | ||
+ | <code cpp> | ||
+ | int x[3] = {1,2,3}; | ||
+ | //int* | ||
+ | auto x1 = x; | ||
+ | // | ||
+ | auto& x2 = x; | ||
+ | </ | ||
+ | ==decltype== | ||
+ | decltype 获取一个表达式,并返回表达式的类型。decltype 与 auto 的区别在于,decltype **不会产生类型退化**。 | ||
+ | |||
+ | * decltype(val):'' | ||
+ | <code cpp> | ||
+ | //x is an variable name | ||
+ | int x = 3; | ||
+ | //int | ||
+ | decltype(x); | ||
+ | // | ||
+ | //int& | ||
+ | decltype((x)); | ||
+ | </ | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | int x = 3; | ||
+ | int& y1 = x; | ||
+ | //int | ||
+ | auto y2 = y1; | ||
+ | //int& | ||
+ | decltype(y1) y3 = y1; | ||
+ | </ | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | int x = 3; | ||
+ | int* ptr = &x; | ||
+ | //*ptr 是包含了解引用操作符的表达式:此处是 l-value,此处得到的是 int& | ||
+ | decltype(*ptr); | ||
+ | </ | ||
+ | * decltype(auto) [C++14]:用于简化 decltype 的使用 | ||
+ | <code cpp> | ||
+ | // | ||
+ | decltype(3.5+15l) x = 3.5 + 15l; | ||
+ | //C++ 14 的写法 | ||
+ | // 用 auto 来代表繁琐的表达式,并不会导致退化 | ||
+ | // 编译器会将 auto 替换为赋值运算符右边的内容 | ||
+ | decltype(autol) x = 3.5 + 15l; | ||
+ | </ | ||
+ | * concept auto [C++20]:任何 concept 都会包含一系列类型,这些类型的共同特征都可以用 concept 表示。比如 '' | ||
+ | * concept auto 让自动推导受 concept 的范围限制,比如让 auto 只能推断出整数类型的类型: | ||
+ | <code cpp> | ||
+ | #include < | ||
+ | int main() | ||
+ | { | ||
+ | //int | ||
+ | std:: | ||
+ | //error, 3.5 is not an integral | ||
+ | std:: | ||
+ | } | ||
+ | </ | ||
+ | ====域和对象生命周期==== | ||
+ | ===域 Scope=== | ||
+ | 域代表了程序的一部分,域中的 name 有**唯一的含义**: | ||
+ | * 全局域:程序最外部的域,全局对象 | ||
+ | * 块域:大括号限定的域,局部对象 | ||
+ | * 其他类型的域:Namespace, | ||
+ | 域可以进行嵌套,内部域中的 Name 会掩盖外部域中的 Name: | ||
+ | <code cpp> | ||
+ | int x = 3; | ||
+ | int main() | ||
+ | { | ||
+ | int x = 4; | ||
+ | // | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | ===对象生命周期=== | ||
+ | 生命周期指对象从被**初始化到被销毁**的区间。 | ||
+ | * 全局对象:生命周期为程序的运行期 | ||
+ | * 局部对象:起始于**初始化**,结束于域的执行完成 | ||
+ |