【C++系列一】
const限定符
const是用来定义不能被改变的变量。
还有一种是利用一个对象去初始化另外一个const对象,那么这个对象是不是const对象都无所谓。
const的限定范围
一般而言,const关键字定义的变量是在文件内部有效。为了在多个文件中可见,那么就需要在多个文件中各自定义同名变量。但是这种做法比较蠢。
那么有时需要const变量在多个文件共享,这个时候可以对const变量的声明和定义都加上extern关键字,这样只需要定义一次即可。
const的引用
说白了就是对常量的引用,但是对常量的引用不能用作修改它所绑定的对象:
const int ci = 1024;
const int& r1 = ci;
r1 = 42; //不能修改;
int &r2 = ci; //也不能这样使用,因为会试得非常量引用指向常量对象,且存在修改风险;
初始化和const的引用
之前说,引用类型必须和所引用对象类型一致,但是这里存在两个例外:
1)在初始化常量引用时允许用任意表达式作为初始值,只要表达式结果能转换成就行;甚至允许一个常量引用绑定非常量对象、字面值和表达式;
int i = 42;
const int& r1 = i; //OK!
本质上是绑定到一个临时量,而非本体;等价于下面:
int i = 42;
const int temp = i;
const int& r1 = temp;
2)对const引用可能引用的并非一个const对象
常量引用只是对引用可参与的操作做出了限制,但是对引用对象本身是不是一个常量未做限定:
int i = 42;
int& r1 = i;
const int& r2 = i; //只是说明不能通过r2去修改i的值;
r1 = 22; //但可以通过其他方式去修改;
指针和const
这个就是一直比较常见的const指针和指针常量的范围;
指针常量:就是指针指向一个常量,这个指针不能用来改变其所指向的对象的值;
常量指针:说的是这个指针是固定死了,但是指向的对象却是可以更改的;
const double pi = 3.14;
const double* ptr1 = π //指针常量;
int errNumb = 0;
int* const ptr2 = &errNumb; //常量指针;
*ptr2 = 1; //OK
顶层const
const本身是不是常量以及指针指向的对象是不是常量,可以将两个名词进行区分:
顶层const:表示指针本身是个常量;
底层const:表示指针所指向的对象是个常量;
更一般的说,顶层const可以表示任意对象是常量;底层const则是与指针和引用等复合类型的基本类型部分有关;指针是个比较特殊的类型,既可以是顶层也可以是底层const。
顶层const和底层const使用主要是在拷贝时候差异明显:
1)顶层const拷贝不受影响;
2)底层const,拷入和拷出对象必须具有相同底层const资格,或者两个对象的数据类型必须能够转换,一般来说是非常量可转常量,反之则不行;
const int ci =42;
const int *p2 = &ci;
const int* const p3 = p2;
int *p = p3; //error
p2 = p3; //OK
p2 = &ci;
constexpr和常量表达式
常量表达式:指值不会改变且在编译期间就已经确认计算结构的表达式;
因为在实际使用过程中,很难分辨一个初始值是不是常量表达式,为了解决这个问题,C++11就推出新关键字constexpr来帮助验证一个变量的值是否为常量表达式,声明为constexpr的变量一定是一个变量,而且必须用常量表达式初始化。
constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr指针
用constexpr声明中定义一个指针,那么限定符constexpr是对指针有效的,与指针的对象无关:
const int* p = nullptr; //底层const
constexpr int* q = nullptr; //顶层const;
类型别名
C中我们习惯用typedef来命名类型别名;
在C++11中,则使用using关键字来命名别名;
using ll = long long;
需要注意复合类型导致的问题:
typedef char* pstring;
const pstring cstr = 0;
const pstring *ps;
这里值得注意的是 cstr其实是一个指向char的常量指针,而非是指向常量字符的指针;
嘿嘿,如果你把pstring用char*带入其中,那么就会得到cstr是指向常量字符的指针,那么就得到截然不同的意思。
auto类型说明符
有的时候是难以清楚地知道表达式的类型,有的时候类型名又臭又长,C++11使用auto关键字来让编译器帮我们去推导类型;
需要值得注意的是用auto推导一行变量时,必须类型一致:
auto sz = 0, pi = 3.14; //error
auto一般会忽略顶层const,同时底层const则会保留下来:
const int ci = i, &cr = ci;
auto b = ci; //int
auto c = cr; //int
auto d = &i; //int*
auto e = &ci; //const int*;
如果希望保留顶层const,那么需要明确指出;
const auto f = ci;
对于引用也是一样的:
auto& g = ci;
auto& h = 42; //error,不能为非常量引用绑定字面值;
const auto& j = 42; //ok
decltype类型指示符
decltype只是计算数据类型,而不做实际计算;
decltype处理顶层const和引用方式与auto有些不一样:
1)如果decltype使用的变量表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内);
2)一般而言,引用都是作为所指对象的同义词出现,只有在decltype时则是一个例外;
如果decltype中的是表达式,则返回根据计算结果对象类型;
在这里需要提醒的是:
对于decltype所用表达式而言,变量名不加上一对括号,则得到的结果就是该变量的类型,如果加上括号(一层或多层),编译器就会将其当成一个表达式,变量可以当做是赋值语句左值的特殊表达式,所以这样decltype就会将其当成一个引用:
decltype((i)) d ; //error, d是int&,需要初始化;
decltype(i) e; //ok, e是int;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】