C++ 学习笔记(6)函数、局部静态对象、重载函数、内联函数、constexpr函数、调试帮助、函数匹配、函数指针

C++ 学习笔记(6)函数、局部静态对象、重载函数、内联函数、constexpr函数、调试帮助、函数匹配、函数指针

参考书籍:《C++ Primer 5th》


6.1 函数基础

6.1.1 局部对象

  • 局部静态对象:在程序执行第一次经过该对象时初始化,直到程序终止才被销毁。
size_t count_calls()
{
	static size_t ctr = 0;
	return ++ctr;
}

int main()
{
	for (size_t i = 0; i < 5; i++)
		cout << count_calls() << endl;
	return 0;
}


6.2 参数传递

  • 形参的初始化方式和变量的初始化方式是一样的。

6.2.1 传值参数

  • 指针形参:拷贝的是指针本身,两个指针指向同一个内容。
void reset(int *ip)
{
	*ip = 0;		// 修改指向内容为0
	ip = 0;		// 改变局部指针ip的指向
}

6.2.3 const形参和实参

  • 当用实参初始化形参时会忽略掉顶层const。如下,传int参数时,下面两个重载函数都参数可以完全一样,相当于重定义。
void func(const int i) { cout << i + 1; }	// 顶层const被忽略,可以传int 或 const int类型。
void func(int i) { cout << i * 3; }			// 运行错误!相当于重复定义func(int)
  • 尽可能使用常量引用:
    • 常量:避免在函数内修改值。同时可以传入普通形参和常量形参。
    • 引用:避免函数对参数拷贝。

6.2.4 数组形参

  • 传递数组形参,实际上是传数组首元素的指针。
// 下面3条语句是完全等价的。如果传入数组或指针,运行时会报错:重定义。
void print(const int*);
void print(const int[]);
void print(const int[10]);

6.2.6 含有可变形参的函数

  • initializer_list形参:可变参数模版,标准库类型。其元素永远是常量。

在这里插入图片描述

void msg(initializer_list<string> il)
{
	for (auto beg = il.begin(); beg != il.end(); ++beg)
		cout << *beg << "  ";
	cout << endl;
}

void main()
{
	msg({ "a","c" });		// 输出:"a  c"
	system("pause");
}

6.3 返回类型和return语句

6.3.2 有返回值函数

  • 返回值的方式和初始化一个变量或形参的方式完全一样。
  • 不要返回局部对象的引用或指针:局部对象在函数调用结束时会被释放。
  • 列表初始化返回值:
vector<string> process() { return{ "fuck","eveything" }; }

6.3.3 返回数组指针

  • Type (* function(parameter_list)) [dimension]
  • 数组类型 ( * 函数名 ( 参数列表 ) ) [ 数组大小 ]
func(int i)			// 普通函数
(*func(int i))			// 对函数结果解引用
(*func(int i))[10]		// 解引用得到一个大小为10的数组
int (*func(int i))[10]	// 数组元素是int类型
  • 使用尾置返回类型:适用于任何函数
auto func(int i) -> int(*)[10];		// 返回指针,指向含有10个整数的数组。
  • 使用decltype。
int arr[] = { 1,2,3,4 };
decltype(arr) * arrPtr(int i);		// 返回指针,指向含有4个整数的数组。

6.4 函数重载

  • 顶层const参数和没有顶层const参数的声明是等价的。
int find(int i);
int find(const i);			// 重定义于上面的函数。

int find(int *ip);
int find(int *const ip);	// 重定义。顶层const。
  • 底层const则可以区分函数重载(指针或引用):
int find(int& i);
int find(const int& i);		// 新函数。底层const。

int find(int*);
int find(const int* i);		// 新函数。底层const。
  • const_cast 和重载:对于参数非常量的的引用,不能使用常量引用。可以在函数内使用const_cast转换。
// 返回结果是常引用。(参数可以是常量,可以是普通变量)
const string &shoterString(const string &s1, const string &s2)
{
	return s1.size() <= s2.size() ? s1 : s2;
}

// 返回结果是普通引用。(参数只能是普通变量)
string &shoterString(string &s1, string &s2)
{
	// 把普通引用强制转换成常引用,才调用前面的函数。
	auto &r = shoterString(const_cast<const string&>(s1), const_cast<const string&>(s2));
	// 返回时,把常引用转回普通引用。
	return const_cast<string&>(r);
}

6.5 特殊用途语言特性

6.5.1 默认实参

  • 在给定作用域内,一个形参只能被赋予一次默认实参。即可以为之前没有默认值的参数设置默认实参,但不能重复设置已经赋予的默认参数。
void screen(int w, int h, string s) { cout << (w * h) << " " + s; }

void screen(int w, int h, string s = "stringgggg");	// 为第三个参数设置默认值

void screen(int w, int h, string s = "asad");		// 运行时错误,重复声明。

void screen(int w = 32, int h = 16, string s);		// 补充前面两个参数的默认值

  • 局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参。

6.5.2 内联函数和constexpr函数

  • 关键字inline,编译过程中,直接在函数调用点上展开。

  • 内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。

  • 很多编译器不支持内联函数的递归。

  • constexpr函数:能用于常量表达式的函数。函数的返回类型及所有形参的类型都得是字面值类型,函数体内有且只有一条return语句。

constexpr int getSize(int i) { return 2 * i; }		// 正确。
constexpr int getSize(int i) {  int a = i; return 2; }		// 错误,不能有其他语句。
  • 内联函数和constexpr函数可以在程序中多次定义,但多个定义必须完全一致,所以通常定义在头文件。

6.5.3 调试帮助

  • assert 是一种预处理宏(preprocessor marco)(预处理变量,类似内联函数,由预处理器管理而非编译器管理)。

  • assert(expr):expr为假(为0)时程序终止。

  • NDEBUG 预处理变量:如果定了这个变量,则assert什么也不做。相当于关闭调试状态。

void print()
{
#ifndef NDEBUG
	cerr << __func__ << endl		// 输出函数名
		<< __FILE__ << endl			// 文件路径
		<< __LINE__ << endl			// 当前行数
		<< __DATE__ << endl			// 日期
		<< __TIME__ << endl;		// 具体时间
#endif // !NDEBUG
}

6.6 函数匹配

  • 候选函数(candidate function):对应的重载函数集。

  • 可行函数(viable function):1.形参数量相等;2.每个实参类型与形参对应相同(或可以转换)。

  • 如果有多个形参匹配,编译器依次检查每个实参,以确定最佳匹配,如果没找到将会报错(二义性)。需要条件:

    • 该函数每个实参的匹配都不劣于其他可行函数需要的匹配。
    • 至少有一个匹配优于其他可行函数。

6.6.1 实参类型转换

  • 转换优先级排序如下:

在这里插入图片描述

  • 一些小整形会直接提升到int:
void ff(int);
void ff(short);
ff('a');			// char直接提升到int,而非short

6.7 函数指针

  • 即指针指向函数而非对象。指向函数类型由返回值和形参类型决定,与函数名无关。
bool pass(const int &score) { return score >= 60; }
bool(*pf)(const int &);		// 定义函数指针,指向的函数类型。

void main()
{
	pf = pass;					// pf指向pass函数
	pf = &pass;					// 等价赋值语句,取地址符是可选的

	cout << pf(58) << endl;		// 等价调用pass(58)
	cout << (*pf)(70) << endl;	// 等价调用pass(70)
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值