复习C++小结(三)——函数

尽量使用常量引用

把函数不会改变的形参定义成普通的引用是一种常见的错误,这不仅会给函数调用者带来一种误导,即函数可以修改它实参的值,还会引起以下的严重错误。

int get_size(string& s)
{
	return s.size();
}

该函数的形参是普通引用,因此不能接受常量引用,由于C++中,字符串字面值是const char* 类型,试图用常量引用初始化非常量引用的变量s将是非法的。例如以下调用非法。

get_size("abc");  // 非法

main处理命令行选项

当使用argv中的实参时,一定要记得可选实参从argv[1]开始;argv[0]保存程序的名字,而非用户输入。

// 调用prog -d -o ofile data0
int main(int argc, char* argv)
{
	// argc = 5;
	// argv[0] == "prog";
	// argv[1] == "-d";
	// argv[2] == "-o";
	// argv[3] == "ofile";
	// argv[4] == "data0";
	// argv[5] == 0;
}

函数返回值作为左值的情况

当函数返回值类型是引用类型时,调用时得到的是左值,其余情况都是右值。

int& get_A_i(int* A, int index)
{
	return A[index];
}
int main()
{
	int A[10];
	get_A_i(A, 1) = 1; // 等价于A[1] = 1;
}

需要注意的是不要返回局部变量的引用,static修饰的局部变量除外,因为他有特殊的生命周期。

int &get_i()
{
	static int i = 243;
	return i;
}

int main()
{
	get_i() = 1; // 执行完毕后i=1;
}

声明一个返回数组指针的函数

int(*func(int i))[10];

可以按照从里往外的方式理解该声明的含义:

  1. func(int i)代表声明的是带有一个int形参的函数,函数名叫func。
  2. (*func(int i))代表返回值是指针类型。
  3. (*func(int i))[10]代表指针指向的是一个长度为10的数组。
  4. int (*func(int i))[10]代表数组中元素的类型是int型。

在C++11中我们可以使用尾置返回类型,从而使上述声明更易理解:

auto func(int i) -> int(*)[10];

把返回值类型放在->的指针之后,就可以让声明更易理解。

重载和const形参

由于顶层const在赋值时没有什么要求,举个例子来说就是int类型可以给const int类型赋值,反过来也可以。所以一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来:

int func(int i);
int func(const int i);

int func2(int *p);
int func2(int *const p);

此处func和func2的两个声明是等价的。但对于底层const来说,就可以区分,比如:

int func(int &i1);
int func(const int &i2);

int func2(int *p1);
int func2(const int *p2);

此时i2和p2的const都是底层const,由于底层const之间不能随意转换,比如作为const int&类型的i2不能初始化int&类型的i1,虽然i1可以初始化i2,但编译器还是有方法选择匹配度更高的函数,因此这样的重载形式是合法的。

关于默认参数

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

int wd = 80;
char def = ' ';
int func() { return 0; }

string screen(int a = wd, char b = def, int c = func())
{
	return "";
}

int main()
{
	screen(); //相当于screen(wd,def,func())
	def = '*';
	int wd = 70; // 隐藏外层wd
	screen(); // 虽然wd被隐藏,但是局部变量wd与函数的默认参数没有任何关系,所以该语句等价于screen(80,'*',func());
}

需要特别注意的是上例中的第二个screen(),虽然定义了一个局部变量wd隐藏外部的wd,但是这个局部变量wd和函数默认参数没有任何关系,所以默认参数wd的值仍然是80。

调试帮助

C++编译器定义了__func__变量它的值是当前调试的函数的名字。除此以外,预处理器还定义了四个对于程序调试很有用的名字:

  • __FILE__存放文件名的字符串字面值。
  • __LINE__存放当前行号的整形字面值。
  • __TIME__存放文件编译时间的字符串字面值。
  • __DATE__存放文件编译日期的字符串字面值。
int main()
{
	cerr << "Error: " << __FILE__
		<< " : in function " << __func__
		<< " at line " << __LINE__ << endl;
}

上面代码输出:

Error: c:\users\Prosat\source\repos\solution\solution\main.cpp : in function main at line 217

所以可以看出编译器提示我们代码错误时所使用的就是这几个变量。

函数指针

定义、赋值和使用
比如我有这样一个函数:

bool lengthCompare(const string &s1, const string &s2)
{
	return s1.size() > s2.size();
}

要定义对应的函数指针,需要确保返回值类型和形参列表精确匹配(能互相转化也不行),对于重载函数来说,我必须指明要调用的是哪个函数。

	// pf指向一个函数,返回值是bool,形参是const string &s1, const string &s2。
	bool(*pf)(const string &s1, const string &s2); // 未初始化
    // 赋值
	pf = lengthCompare; // 合法,会自动将lengthCompare的地址赋值给pf。
	pf = &lengthCompare; // 等价于上面的语句
    // 调用
    bool b1 = pf("hello", "goodbye");
	bool b2 = (*pf)("hello", "goodbye"); // (*pf)括号不能少,否则含义不同。

返回指向函数的指针

int(*f3(int i))(int *a, int b);

同样,我们采用从里往外的方式理解该声明的含义:

  1. f3(int i)代表声明的是带有一个int形参的函数,函数名叫f3。
  2. (*func(int i))代表返回值是指针类型。
  3. int (*func(int i))(int *a,int b)代表指针指向的是一个形参为(int *a,int b)返回值为int的函数。

对应的尾指返回类型声明:

auto f3(int i) -> int(*)(int *a, int b);

个人感觉尾置返回类型真的容易理解多了,看上去很明了。

发布了6 篇原创文章 · 获赞 0 · 访问量 89

猜你喜欢

转载自blog.csdn.net/qq_33584870/article/details/104693434