(1)函数:反复调用的代码
(2)main函数:特殊的函数,作为整个、程序的入口
(3)类型
(4)语句:表明需要执行的操作
(5)注释
单行注释
多行注释
(1)iostream:标准库所提供的IO接口,用于用户交互
使用 <>:表示从系统环境变量所指定的位置中寻找
使用" ":表示从当前项目下寻找
输入流:cin;输出流:cout/cerr/clog
cerr与clog的区别:是否立即刷新缓冲区,cerr(错误信息)立即刷新缓冲区,clog(日志信息)不立即刷新缓冲区。
缓冲区刷新:std::flush,刷新加换行使用std::endl
(2)名字空间:用于防止名称冲突
std 名字空间:C++标准库提供包含在std中
访问名字空间中元素的三种方式:域解析符:: ,using语句(有名字冲突的可能),名字空间别名
名字空间与名称改编(name mangling):文件链接时不希望存在::,可能会对链接产生影响,所以对名称进行变换
如果使用C的系统IO,则需要包含 cstdio
C/C++系统IO比较:
- printf:使用直观但容易出错
- cout:不易出错但书写较长
- C++20格式化库:新的解决方案,解析为字符串
(1)if语句
(2)==与=
= :赋值操作
== :判断两个值是否相等
将常量放在==左边防止误用
(3)while语句
(1)结构体:将相关数据放置在一起
-通过点操作符访问内部数据
-可以作为函数的输入参数或返回类型
-可以引入成员函数,更好地表示函数与数据的相关性
初始化/赋值:功能是将某个值与一个对象关联起来
-值:字面值1,2;对象(变量或常量)所表示的值x=y
-标识符:变量,常量,引用
-初始化基本操作:
在内存中开辟空间,保存相应的数值、
在编译器中构造符号表,把标识符与相关内存空间关联起来
-值与对象均有类型
-初始化/赋值可能涉及类型转换(类型不匹配)
(1)类型是一个编译器概念,可执行文件不存在类型的概念
(2)C++是强类型语言
(3)引入类型是为了更好地描述程序,防止误用
(4)类型具体描述了:
1)存储所需要的尺寸(sizeof)
2)取值空间(std::numeric_limits,超过范围可能产生溢出)
3)对齐信息(alignof)对结构体的大小产生影响
4)可执行的操作
(5)类型分类:基本类型与复杂类型
1)基本(内建)类型:
数值类型:字符(char, wchar_t, char16_t, char32_t),整数,浮点数
void
2)复杂类型
基本类型组合、变种所产生的类型,可能是标准库引入,或自定义类型
(6)与类型相关的标准未定义部分
1)char 是否有符号
char:不确定
unsinged char:无符号
singed char:有符号
2)整数中内存中的保存方式:大端,小端
3)类型的大小会间接影响取值范围
C++11 中引入了固定尺寸的整数类型,如 int32_t,保证可移植性
(1)字面值:程序中直接表示为一个具体数值或字符串的值
(2)每个字面值都有类型
1)整数字面值:20(十进制) 024(八进制)0x14(十六进制)
2)浮点数:1.3
3)字符字面值:‘c’,‘\n’
4)字符串字面值:“Hello” char[6]型
5)布尔字面值:true , false
6)指针字面值:nullptr -nullptr_t型
(1)变量对应一段存储空间,可以改变其中的内容
(2)变量的类型在其首次声明(定义时)指定
变量声明与定义的区别:文件外已定义的的变量在文件内使用需要声明,在变量名前加extern前缀
(3)变量的初始化与赋值
1)初始化
缺省初始化
直接int x(10)/拷贝初始化int x = 10
其他初始化
(1)变量赋值时涉及到的类型转换
1)bool与整数之间的转换
2)浮点数与整数
(2)隐式类型转换不只发生在赋值时
1)if判断
2)赋值比较:unsinged与int类型比较时,int会转换成unsigned int
(1)指针:一种间接类型
1)特点
可以指向不同对象
具有相同的尺寸
2)操作
&:取地址
*:解引用
(2)指针的定义
(3)nullptr
1)一个特殊的对象(类型为nullptr_t),表示为空指针
2)类似与C中NULL,但更加安全
(4)指针与bool类型的隐式转换
true:指向非空
false:指向为空
(5)主要操作:解引用,增加,减少,判等
(6)void* 指针
没有记录对象的尺寸信息,可以保存任意地址
支持判等操作
(7)指针的指针
不太常用
(8)引用
int x = 3;
int& ref = x;
ref 为 x 的别名(ref 是对象的别名,不能绑定到字面值 )
(1)声明
const
(2)编译器保证不可写
保证:防止非法操作,优化程序逻辑
(3)常量指针与指针常量
常量指针:指向常量的指针 const int* ptr = &x;
指针常量:int* const ptr = &x;
(4)常量引用
const int&
主要用于函数形参(通常用于较大对象,如结构体或类)
可以绑定字面值:const int & ref = 3;
(5)常量表达式(C++11新标准)
使用 constexpr 声明
声明的是编译期常量
int x;
std::cin >> x;
const int y1 = x; //y1的值运行期确定
//const int y2 = 3; //y2的值编译期确定
constexpr int y2 = 3; //更易进行后续优化
if(y1 == 3)
{
}
if(y2 == 3)
{
}
常量表达式指针:constexpr const int* ptr = nullptr;
其中,ptr 的类型为: const int* const
//1
typedef int myInt;
typedef char myCharArr[4];
//2从c++11开始
using myInt = int;
using myCharArr = char[4];
类型别名与指针引用的关系:
应将指针类型别名视为一个整体,在此基础上引入常量表示指针为常量的类型
using IntPtr = int*;
int main(){
int x = 3;
const int* ptr = &x; //const修饰int
const IntPtr ptr = &x; //等价于int* const ptr
}
不能通过类型的别名构造引用的引用
常见形式:
●auto: 最常用的形式,但会产生类型退化
● const auto / constexpr auto: 推导出的是常量 / 常量表达式类型
● auto& : 推导出引用类型,避免类型退化
● decltype(exp) :返回 exp 表达式的类型(表达式若为左值则加引用)
● decltype(val) :返回 val 的类型
● decltype(auto) :从 c++14 开始支持,简化 decltype 使用
● concept auto :从 C++20 开始支持,表示一系列类型( std::integral auto x = 3; )
#include
int main()
{
int x = 3;
int* ptr = &x;
const int y1 = 3;
const int& y2 = y1;
std::cout << std::is_same_v<decltype(3.5 + 15l), double> << std::endl;
std::cout << std::is_same_v<decltype(*ptr), int&> << std::endl;
std::cout << std::is_same_v<decltype(ptr), int*> << std::endl;
std::cout << std::is_same_v<decltype(x), int> << std::endl;
std::cout << std::is_same_v<decltype((x)), int&> << std::endl;
std::cout << std::is_same_v<decltype(y1), const int> << std::endl;
std::cout << std::is_same_v<decltype(y2), const int&> << std::endl;
std::cout << std::is_same_v<decltype((y1)), const int&> << std::endl;
std::cout << std::is_same_v<decltype((y2)), const int&> << std::endl;
decltype(auto) x = 3.5 + 15l;
}
域(scope)
全局域(global scope):程序最外围的域,其中定义的为全局变量。
块域(block scope):使用大括号所限定的域,其中定义的是局部对象。
1)指针数组
int x1;
int x2;
int x3;
int *a[3] = {&x1, &x2, &x3};
2)数组的指针
int (*a)[3]; //a得到类型为int(*)[3]
3)数组的引用
int b[3];
int (&a)[3] = b; //a的类型int(&)[3]
使用时通常会转换成相应的指针类型
x[y] --> *((x) + (y))
使用数组对象时,通常会产生数组到指针的隐式转换,隐式转换会丢失一部分类型信息
可通过声明引用避免隐式转换
int a[3] = {1,2,3}; //a的类型为int[3]
auto b = a; //b的类型为int*
auto& b = a; //b的类型为int(&)[3]
source.cpp
int array[3] = {1,2,3};
void fun()
{
std::cout << array << std::endl; //输出数组元素首地址
}
main.cpp
void fun(); //声明
extern int array[]; //unknown Bounded Array
//int array[] 不完整声明
int main()
{
fun();
std::cout << array << std::endl;
std::cout << array[0] << std::endl;
}
获得指向数组开头与结尾的指针 : std::©begin, std::©end
int a[3] = {1,2,3};
std::cout << a << ' ' << &(a[0]) << ' ' << std::begin(a) << std::endl;
std::begin(a); //int*
std::cbegin(a); //const int*
1)求元素个数
int a[3];
std::cout << sizeof(a) / sizeof(int) << std::endl;
int a[3];
std::cout << std::size(a) << std::endl; //推荐
int a[3];
std::cout << std::end(a) std::begin(a) << std::endl;
2)元素遍历
//基于元素个数
int a[4] = {2, 3, 5, 7};
size_t index = 0;
while(index < std::size(a))
{
std::cout << a[index] << std::endl;
index = index +1;
}
//基于 (c)begin/(c)end
int a[4] = {2, 3, 5, 7};
auto ptr = std::cbegin(a);
while(ptr != std::cend(a))
{
std::cout << *ptr << std::endl;
ptr = ptr + 1;
}
//基于 range-based for 循环
for (int x : a)
{
std::cout << x << std::endl;
}
3)c字符串
#include
-------------------------------------
char str[] = "Hello";
//char str[] = {'H', 'e', 'l', 'l', 'o', '\0'};
auto ptr = str;
std::cout << strlen(str) << std::endl;
std::cout << strlen(ptr) << std::endl;
1)本质:数组的数组
2)索引与遍历
int x[3][4] = {1, 2, 3, 4, 5};
for (auto& p: x)
{
for (auto& q: p)
{
std::cout << q << '\n';
}
}
int x[3][4] = {1, 2, 3, 4, 5};
size_t index0 = 0;
while(index0 < 3)
{
size_t index1 = 0;
while(index1 < 4)
{
std::cout << x[index0][index1] << std::endl;
index1 = index1 + 1;
}
index0 = index0 + 1;
}
int x[3][4] = {1, 2, 3, 4, 5};
size_t index0 = 0;
while(index0 < std::size(x))
{
size_t index1 = 0;
while(index1 < std::size(x[index0]))
{
std::cout << x[index0][index1] << std::endl;
index1 = index1 + 1;
}
index0 = index0 + 1;
}
3)指针与多维数组
多维数组可以隐式转换为指针,但只有最高维会进行转换,其它维度的信息会被保留。
int x[3][4];
auto ptr = x; //等价于 int (*ptr)[4] = x
使用类型别名来简化多维数组指针的声明。
using A2 = int[4][5];
using A = int[4];
int mian()
{
int x[3][4][5];
//auto ptr = x;
A2* ptr = x;
//int x2[3][4];
A x2[3];
A* ptr2 = x2;
}
使用指针遍历多维数组。
int main()
{
int x[3][4];
auto ptr = std::begin(x);
while (ptr != std::end(x))
{
auto ptr2 = std::begin(*ptr);
while (ptr2 != std::end(*ptr))
{
std::cout << *ptr2 << std::endl;
ptr2 = ptr2 + 1;
}
ptr = ptr + 1;
}
}
std::vector<int> x1(3,1);
std::vector<int> x2{1,1,1};
1获取元素个数,判断是否为空
std::cout << x1.size() << std::endl;
std::cout << x1.empty() << std::endl;
2插入删除元素
x1.push_back(2);
x1.pop_back();
3vector比较
std::vector<int> x1 = {1, 2, 3};
std::cout << x1[2] << std::endl;
std::cout << x1.at(2) << std::endl;
1模拟指针的行为
2vector对应随机访问迭代器
解引用与下标访问
移动
两个迭代器相减求距离
两个迭代器比较
1添加元素可能使迭代器失效
2多维vector
std::vector<std::vector<int> > x;
x.push_back(std::vector<int>());
x[0].push_back(1);
std::cout << x[0][0] << std::endl;
3从 . 到 ->
std::vector<int>* ptr = &x;
std::cout << (*ptr).size() << std::endl;
std::cout << ptr->size() << std::endl;
4Member type
size_type
iterator/const_iterator
#include
#include
int main()
{
std::string x("Hello"); //初始化
std::string y("Hello World");
std::cout<< (y < x) << std::endl;
y = y + x; //赋值
std::cout << y << std::endl;
}
1尺寸相关
2比较
3赋值
4拼接
5索引
6转换为c字符串
(1) 所有的划分都是针对表达式的,不是针对对象或数值。
**泛左值 (glvalue)**(“泛化 (generalized)”的左值)是求值确定一个对象、位域或函数的个体的表达式。 可以理解为获取x所关联的内存
int x;
x = 3
**纯右值 (prvalue)**(“纯 (pure)”的右值)是求值符合下列之一的表达式:
计算某个运算符的操作数的值(这种纯右值没有*结果对象*)
x = 3 //3为纯右值
初始化某个对象(称这种纯右值有一个*结果对象*)
int x = 3;
**亡值 (xvalue)**(“将亡 (expiring)”的值)是代表它的资源能够被重新使用的对象或位域的泛左值。
#include
#include
void fun(std::vector<int>&& par)
{
}
int main()
{
std::vector<int> x;
fun(std::move(x)); //std::move(x)将x转化为将亡值
//....后续不会再使用x,可对par重复利用
}
(2)左值与右值的转换
左值转换为右值( lvalue to rvalue conversion )
int x = 3;
int y;
y = x;
临时具体化( Temporary Materialization )prvalue to xvalue
struct Str
{
int x;
};
int main()
{
Str(); //纯右值,不需关注内存
Str().x; //Src()被视为将亡值,才可谈论内部的信息
}
void fun(const int& par)
{
}
int main()
{
fun(3);
}
(3)decltype
decltype ( 表达式 )
a) 如果 表达式 的值类别是亡值,将会 decltype
产生 T&&
;
#include
#include
int main()
{
int x;
decltype(std::move(x)) y = std::move(x);
}
b) 如果 表达式 的值类别是左值,将会 decltype
产生 T&
;#include
#include
int main()
{
int x;
decltype((x)) y = x;
}
#include
int main()
{
int x;
int & y = x;
return 0;
}
c) 如果 表达式 的值类别是纯右值,将会 decltype
产生 T
。
#include
int main()
{
decltype(3) x;
}
#include
int main()
{
int x;
return 0;
}
(1)隐式类型转换
自动发生
实际上是一个(有限长度的)转型序列
[隐式转换 - cppreference.com](https://zh.cppreference.com/w/cpp/language/implicit_conversion)
(2)显式类型转换(避免使用)
static_cast<double>(3) + 5;
const int* ptr;
const_cast<int*>(ptr);
int x = 3;
double y = reinterpret_cast<double>(x); //错误,无法编译
//正确写法,但结果可能不是想要的
int* ptr = &x;
double* ptr2 = reinterpret_cast<double*>(ptr);
1操作数与结果均为算数类型的右值;但加减法与一元 + 可接收指针。
2一元 + 或 - 操作符会产生 integral promotion
int main()
{
short x = 3;
auto y = -x;
}
int main()
{
short x = 3;
int y = +static_cast<int>(x);
return 0;
}
(1) 逻辑与、逻辑或具有短路特性
int main()
{
int* ptr = nullptr;
if (ptr && (*ptr == 3)) //ptr为空的话不会判断 *ptr是否为3
{
}
}
(2)逻辑与的优先级高于逻辑或
(3)不能将多个关系操作符串连
int a = 3;
int b = 4;
int c = 5;
std::cout << (c > b > a) << std::endl;
//c > b > a --> c > b true > a --> 1 > a --> false
//可以写成
std::cout << ((c > b) && (b > a)) << std::endl;
(4)不要写出 val == true 这样的代码
(5)Spaceship operator:<=>
– strong_ordering
int main()
{
int a = 5;
int b = 4;
auto res = (a <=> b);
if (res > 0) //也可写成 res == std::strong_ording::greater
{
}
else if (res < 0) //res == std::strong_ording::less
{
}
else if (res = 0) //res == std::strong_ording::equal
{
}
}
#include
int main()
{
int a = 5;
int b = 4;
std::strong_ordering res = (a <=> b);
return 0;
}
– weak_ordering
– partial_ordering
#include
#include
#include
int main()
{
double f = 3.0;
auto res (sqrt(-1) <=> 5.0);
std::cout << (res > 0) << std::endl; //0
std::cout << (res < 0) << std::endl; //0
std::cout << (res == 0) << std::endl; //0
std::cout << (res == std::partial_ordering::unordering)
<< std::endl; //1
}
接收右值,进行位运算,返回右值
● 除取反外,其它运算符均为左结合的
● 注意计算过程中可能会涉及到 integral promotion
● 注意这里没有短路逻辑
● 移位操作在一定情况下等价于乘(除) 2 的幂,但速度更快
● 注意整数的符号与位操作符的相关影响
– integral promotion 会根据整数的符号影响其结果
– 右移保持符号,但左移不能保证
● 左操作数为可修改左值;右操作数为右值,可以转换为左操作数的类型
● 赋值操作符是右结合的
● 求值结果为左操作数
● 可以引入大括号(初始化列表)以防止收缩转换( narrowing conversion )
● 小心区分 = 与 ==
● 复合赋值运算符
● ++; –
● 分前缀与后缀两种情况
● 操作数为左值;前缀时返回左值;后缀时返回右值
● 建议使用前缀形式
(1)成员访问操作符: . 与 ->
1 -> 等价于 (*).
2 . 的左操作数是左值(或右值),返回左值(或右值 xvalue )
左值:
#include
struct Str
{
int x;
};
int main()
{
Str a;
decltype((a.x)) y = a.x;
return 0;
}
#include
struct Str
{
int x;
// inline Str() noexcept = default;
};
int main()
{
Str a = Str();
int & y = a.x;
return 0;
}
右值
#include
struct Str
{
int x;
};
int main()
{
Str a;
decltype((Str().x)) y = std::move(a.x);
}
#include
struct Str
{
int x;
// inline Str() noexcept = default;
};
int main()
{
Str a = Str();
int && y = std::move(a.x);
return 0;
}
3 -> 的左操作数指针,返回左值
#include
struct Str
{
int x;
};
int main()
{
Str a;
Str* ptr = &a;
decltype((ptr->x)) y = a.x;
};
#include
struct Str
{
int x;
// inline Str() noexcept = default;
};
int main()
{
Str a = Str();
Str * ptr = &a;
int & y = a.x;
return 0;
}
(2)条件操作符 ? :
唯一的三元操作符
接收一个可转换为 bool 的表达式与两个类型相同的表达式,只有一个表达式会被求值
如果表达式均是左值,那么就返回左值,否则返回右值
右结合
int main()
{
int score = 100;
int res = (score > 0) ? 1 : (score == 0) ? 0 : -1;
std::cout << res << std::endl;
}
#include
#include
int main()
{
std::vector<int> arr{1, 2, 3, 4, 5};
for (int v : arr)
{
std::cout << v << "\n";
}
}
使用常量左值引用读元素;
#include
#include
int main()
{
std::vector<std::string> arr{"h", "e", "l"};
for (const std::string& v : arr)
{
std::cout << v << "\n";
}
}
#include
#include
int main()
{
std::vector<std::string> arr{"h", "e", "l"};
for (const auto& v : arr)
{
std::cout << v << "\n";
}
}
使用 万能引用( “ universal reference )”修改元素
#include
#include
int main()
{
std::vector<int> arr{1, 2, 3, 4, 5};
for (auto& v : arr)
{
v = v + 1;
}
}
#include
#include
int main()
{
std::vector<int> arr{1, 2, 3, 4, 5};
for (auto&& v : arr) //万能引用
{
v = v + 1;
}
}
处理无法整除的情形
额外增加一个循环语句
将 switch 与循环结合 ——达 夫设备
#include
#include
int main()
{
constexpr size_t buffer_count = 10001;
std::vector<size_t> buffer(buffer_count);
for (stze_t i = 0; i < buffer_count; i++)
{
buffer[i] = i;
}
size_t max_value = buffer[0];
auto ptr = buffer.begin();
size_t i = 0
switch (buffer_count % 8)
for( ; i < (bufffer_count + 7) / 8; i++)
{
[[fallthrougth]];
case 0: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 7: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 6: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 5: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 4: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 3: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 2: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
[[fallthrougth]];
case 1: max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
}
std::cout << max_value << "\n";
}
(1)函数传值,传地址,传引用
(2)函数传参过程中的类型退化
#include
void fun (int * par)
{
}
int main()
{
int a[3];
fun(a);
}
(3)变长参数
initializer_list
#include
#include
void fun(initializer_list<int> par)
{
}
int main()
{
fun({1, 2, 3, 4});
}
可变长度模板参数
使用省略号表示形式参数
(4)函数可以定义缺省实参
如果某个形参具有缺省实参,那么它右侧的形参都必须具有缺省实参
在一个翻译单元中,每个形参的缺省实参只能定义一次
具有缺省实参的函数调用时,传入的实参会按照从左到右的顺序匹配形参
缺省实参为对象时,实参的缺省值会随对象值的变化而变化
(5)main 函数的两个版本
无形参版本
带两个形参的版本
int main(int argc, char* argv[])
{
}
(1)函数返回
隐式返回
显式返回关键字: return
返回值优化( RVO )—— C++17 对返回临时对象的强制优化
struct Str
{
Str(int) {}
Str(const Str&)
{
std::cout << "拷贝构造初始化\n";
}
};
Str fun()
{
Str x{3};
return x; //如果return x{3} 则C++17后强制优化
}
int main()
{
Str res = fun();
}
(2)返回类型
1) 返回类型的几种书写方式
1 经典方法:位于函数头的前部
int fun(int a, int b)
{
return a + b;
}
2 C++11 引入的方式:位于函数头的后部
auto fun(int a, int b) -> int
{
return a + b;
}
3 C++14 引入的方式:返回类型的自动推导
int fun(int a, int b)
{
return a + b;
}
使用 constexpr if 构造 具有不同返回类型 的函数
constexpr bool value = true;
auto fun()
{
if constexpr(value)
{
return 1;
}
else
{
return 3.14;
}
}
2)返回类型与结构化绑定( C++ 17 )
#include
struct Str
{
int x;
int y;
};
Str fun()
{
return Str{};
}
int main()
{
auto [v1, v2] = fun();
v1;
v2;
}
#include
struct Str
{
int x;
int y;
};
Str fun()
{
return Str{0, 0};
}
int main()
{
Str __fun15 = fun();
int & v1 = __fun15.x;
int & v2 = __fun15.y;
v1;
v2;
return 0;
}
3)[[nodiscard]] 属性( C++ 17 )
#include
[[nodiscard]] int fun(int a, int b)
{
return a + b;
}
int main()
{
fun(2, 3);
//int x = fun(2, 3);
//std::cout << x << std::endl;
}
(1)函数重载
(2)名称查找
限定查找( qualified lookup )
#include
void fun()
{
std::cout << "global fun is called.\n";
}
namespace MyNs
{
void fun()
{
std::cout << "MyNs fun is called.\n";
}
}
int main()
{
::fun();
MyNs::fun();
}
非限定查找( unqualified lookup )
非限定查找会进行域的逐级查找 名称隐藏( —— hiding )
#include
void fun()
{
std::cout << "global fun is called.\n";
}
namespace MyNs
{
void fun()
{
std::cout << "MyNs fun is called.\n";
}
void g()
{
fun();
}
}
int main()
{
MyNs::g();
}
实参依赖查找( Argument Dependent Lookup: ADL )
只对自定义类型生效
namespace MyNs
{
struct Str{};
void g(Str x)
{
}
}
int main()
{
MyNs::Str obj;
g(obj);
}
(3)重载解析
在名称查找的基础上进一步选择合适的调用函数
1过滤不能被调用的版本
参数个数不对
无法将实参转换为形参
实参不满足形参的限制条件
void fun(int x)
{
}
void fun(std::string x)
{
}
int main()
{
fun(3);
}
2在剩余版本中查找与调用表达式最匹配的版本,匹配级别越低越好(有特殊规则)
级别 1 :完美匹配 或 平凡转换(比如加一个 const )
级别 2 : promotion 或 promotion 加平凡转换(如short -> int)
级别 3 :标准转换 或 标准转换加平凡转换(如int -> double)
级别 4* :自定义转换 或 自定义转换 加平凡转换 或 自定义转换加标准转换(与类相关)
级别 5* :形参为省略号的版本
函数包含多个形参时,所选函数的所有形参的匹配级别都要优于或等于其它函数
void fun(int x, int y)
{
}
void fun(int x, double y)
{
}
void fun(bool x, float y) //1, 3
{
}
void fun(int x, double y) //2, 1
{
}
int main()
{
fun(true, 1.0); //有歧义
fun(true, 1.0f);
fun(static_cast<int>(true, 1.0);
}
(1)递归函数
通常用于描述复杂的迭代过程
(2)内联函数
通常在.h文件中定义内联函数,使得函数从程序级别的一处定义转化为翻译单元的一次定义。
(3)constexpr 函数 (C++11 起 )
#include
constexpr int fun() //函数可在编译期执行,也可在运行期执行
{
return 3;
}
constexpr int x = fun();
int main()
{
}
consteval 函数(C++20起)
只能在编译器求值
(4)函数指针
函数指针类型
int main()
{
//引入:数组的指针类型
using K = int[3];
K * a; //等价于int (*a) [3],a是一个指针,指向包含3个int的数组
int * a[3] //a是一个数组,包含3个元素,每个元素是int* 类型的对象
}
int inc(int x)
{
return x + 1;
}
int dec(int x)
{
return x + 1;
}
using K = int(int);
int main()
{
K * fun = &inc;
std::cout << (*fun)(100) << std::endl;
}
int inc(int x)
{
return x + 1;
}
int dec(int x)
{
return x + 1;
}
using K = int(int);
int Twice(K * fun, int x)
{
int temp = (*fun)(x);
return temp * 2;
}
int main()
{
std::cout << Twice(&inc, 100) << std::endl;
std::cout << Twice(&dec, 100) << std::endl;
}
函数指针与重载
void fun(int)
{
}
/*
void fun(int, int)
{
}
*/
int mian()
{
auto x = fun; //退化为函数指针类型
}
void fun(int)
{
}
void fun(int, int)
{
}
int mian()
{
//auto x = fun; //两个不同的fun函数类型,无法隐式转换
using K = void(int);
K * x = fun;
}
将函数指针作为函数参数
将函数指针作为函数返回值
int inc(int x)
{
return x + 1;
}
int dec(int x)
{
return x + 1;
}
auto fun(bool input)
{
if (input)
return inc;
else
return dec;
}
int main()
{
std::cout << (*fun(true))(100) << std::endl;
}