c++语法拾遗,一些细节与特性
2022/4/1 22:49:48
本文主要是介绍c++语法拾遗,一些细节与特性,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
写了2年多的C+STL的acmer,在学习《C++ primer》时总结的一些少见的语法特性与细节。总体还是和题目说的一样这是一篇 c++ 拾遗。
1 变量和基本类型
1.1 基本类型
1.1.1 字面常量
0123 表示的不是带有前导0的数字123,而是代表8进制数字123。
1.2 常量
1.2.1 constexpr
constexpr变量能自动判别赋值的表达式是否是常量表达式,若不是则会报错。
constexpr int mf=20; //20是常量表达式 constexpr int limit=mf+1; //mf+1是常量表达式 constexpr int sz=size(); //当size是一个constexpr 函数的时候是是常量表达式 int a=20; constexpr int b=a+1; //报错
1.2 处理类型
1.2.1 decltype
decltype能和auto类似的自动判别类型。
decltype(f()) a=b; //这里的a的类型是f()返回值的类型
编译器并不实际调用函数f,只是将返回值作为a的类型。
实际上decltype(exp) exp是一个表达式所以可以这么写
int a = 0; decltype(a) b = 1; //b 被推导成了 int decltype(10.8) x = 5.5; //x 被推导成了 double decltype(x + 100) y; //y 被推导成了 double
- 如果 exp 是一个不被括号( )包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致。
- 如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。
- 如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用。
2 表达式
2.1 基础
2.1.1 左值
简单的一句话说,左值就是内存。
2.1.2 右值
简单的一句话说,右值就是数据。
2.1.3 求值顺序
c++ 中没有定义表达式的求值顺序,如:
int cnt=0; int f(){ return ++cnt; } int main(){ int a=f()-f(); //a的结果可能会因为编译器不同而不同 //因为两个f函数的调用顺序没有定义 return 0; }
int a=1; int b=a+a++; //同样,i的自增和i的调用顺序没定义 int c=a++ + ++a; //同样无定义 int *d= new int[2]; *d=f(*d++); //无定义
另外java是有定义表达式的求值顺序的,从左到右。c++不定义是为了编译器启用更多优化。
3 函数
3.1 可变参数的函数
3.1.1 initializer_list 形参
int sum(initializer_list<int> li){ int s=0; for(auto i : li) s+=i; return s; } // 调用方法 sum({1,2,3,4,5}); sum({1,1,1,1});
initializer_lsit 对象中的元素永远是常量类型。
可使用begin、end、size方法进行操作。
3.1.2 省略符形参
void f(...); void f(cnt,...);
不建议使用,仅为方便c++访问某些c代码设置。
3.2 函数返回
3.2.1 值如何被返回的
值返回的时候回调用拷贝构造函数,创建一个临时对象。
3.2.2 列表返回值
C++11 新标准规定,函数可以返回花括号包围的值的列表。
vector<int> f(){ return {1,2,3,4}; }
实际上就是花括号赋值,因为值返回的时候回调用拷贝构造函数,如果拷贝构造函数支持花括号,那就可以作为返回值。
3.2.3 返回数组指针
int (*f)()[size]; //表示函数f返回长度为size的数组指针 //特别的是这里数组表示是后置的
3.2.4 尾值返回类型
在C++11标准中添加了一种尾置返回类型的定义。
auto f()->int; //同等于 int f(); auto f()->int *[size]; //同等于 int (*f)()[size];
3.3 重载
3.3.1重载与const 形参
顶层的const无法和另一个没有顶层的const的参数区分,但底层的const可以区分
void f(int); void f(const int); //重复声明,报错 void f(int *) void f(int *const) //重复声明 void f1(int*); void f1(const int*); //底层const,新函数。 void f1(int&); void f1(const int&); //底层const,新函数。
3.3.2 重载与作用域
当在作用域内重复定义标识符,作用域内的标识符将会直接覆盖外部的标识符,包括重载的。
void f(int); //函数1 void f(char); //函数2 void f(double); //函数3 void foo(){ void f(double); //函数4 f(1.2); //正确调用函数4 f(1); //正确调用函数4,传入值为1.0 f('c') //错误,没定义 }
3.4 特殊用途语言特性
3.4.1 默认实参
可给函数的参数设置默认实参,但缺省的调用时,会默认赋值。
void f(int a,int b,int c=1,int d=2,int e=3); //声明 //调用 f(1,2); //实际为f(1,2,1,2,3); f(1,2,3); //实际为f(1,2,3,2,3); f(1,2,3,4,5); //实际为f(1,2,3,4,5);
设置默认实参只能从右到左,并且不能留空。
void f(int a=1,int b=2); //正确 void f(int a=1,int b); //错误 void f(int a,int b=1,int c); //错误
默认值可赋函数、变量或者表达式。
int x=1; int v(); void f(int a=x,int b=v()); // 将在调用f时,调用v,为函数参数赋值
3.4.2 内联函数 inline
在函数的返回类型前面加上关键字inline,就可以将函数声明为内联函数。内联函数有点类似c的宏函数,它会在每一个调用的位置,内联的展开,从而节省函数调用和返回的开销。但内联函数声明只是向编译器发出的请求,编译器可以选择忽略。
inline void f();
内联函数定义通常放在头文件中
3.4.3 constexpr 函数
该函数相当于一个常量表达式。
constexpr int f();
constexpr 函数定义通常放在头文件中
3.5 调试帮助
3.5.1 assert 预处理宏
assert(expr);
当expr为假时输出信息并终止程序。
3.5.2 NDEBUG 预处理变量
当定义NDEBUG后,表示关闭调试。
自定义调试
#ifndef NDEBUG //调试代码 #endif
对调试有帮助的标识符
- __func__ 当前函数名 字符串
- __FILE__ 文件名 字符串
- __LINE__ 当前行号 整型
- __TIME__ 编译时间 字符串
- __DATE__ 编译日期 字符串
这篇关于c++语法拾遗,一些细节与特性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-15PingCAP 黄东旭参与 CCF 秀湖会议,共探开源教育未来
- 2024-05-13PingCAP 戴涛:构建面向未来的金融核心系统
- 2024-05-09flutter3.x_macos桌面os实战
- 2024-05-09Rust中的并发性:Sync 和 Send Traits
- 2024-05-08使用Ollama和OpenWebUI在CPU上玩转Meta Llama3-8B
- 2024-05-08完工标准(DoD)与验收条件(AC)究竟有什么不同?
- 2024-05-084万 star 的 NocoDB 在 sealos 上一键起,轻松把数据库编程智能表格
- 2024-05-08Mac 版Stable Diffusion WebUI的安装
- 2024-05-08解锁CodeGeeX智能问答中3项独有的隐藏技能
- 2024-05-08RAG算法优化+新增代码仓库支持,CodeGeeX的@repo功能效果提升