[cpp primer随笔] 09. 形参列表细则

本篇分享一些在函数声明中,形参列表需要注意的一些问题。

一、常量引用形参

在函数调用时,普通引用类型的形参无法传入const对象字面值以及需要类型转换的对象进行初始化,此时必须使用常量引用作为形参类型。

struct A{
	int count;
};

void test1(A &a);
void test2(const A &a);

A a{10};
test1(a); // 正确,普通引用绑定对象实参
test1(A{10}); // 不正确,普通引用形参无法绑定字面值
test2(A{10}); // 正确,常量引用形参可以绑定字面值实参

二、数组形参

  1. 数组指针形参
    将数组用作函数形参时需要考虑它的两种性质,其一是数组无法拷贝,其二是数组名在相当多的情形下会被自动转化为数组首元素的指针。因此,**请谨记,函数形参列表中的函数类型将会被编译器直接转化为指针类型。
    // 以下三种函数声明等价,形参类型均为const int*
    void print(const int*);
    void print(const int[]);
    void print(const int[10]);
    
    第三种函数声明,尽管指示出了该数组应当具有10个元素,但是并不起实际作用,仍旧会被编译器视作const int*来处理。
    由于传入的是数组元素指针,因此该形参不具备数组维度信息,在这种情况下想要完成数组遍历或元素访问,可借助以下三种形式:
    • 其一,是在数组中设置明显的结束标志。类似于C风格字符串以空字符结尾。
    • 其二,是以数组的起始和结尾迭代器作为形参类型进行传入,以迭代器的姿态进行遍历。
    • 其三,添加数组维度形参,显式传入数组大小。
  2. 数组引用形参
    区别于指针形参,数组引用类型的形参可以明确绑定一个完整的数组类型,并且在该函数中,还可以使用范围for循环对该数组形参进行遍历。
    // 该形参类型是一个有10个整型元素数组的引用
    void print(int(&arr)[10]){
    	for(auto &elm: arr){ // 对完整数组类型可使用范围for进行遍历
    	 // 元素处理逻辑
    	}
    }
    
  3. 多维数组形参
    将多维数组作为实参,此时数组第二维及之后的维度不可忽略。这与普通数组指针形参不同,普通数组指针形参的维度是被忽略的,而在多维数组中只有第一维会被忽略。例如,在下面的函数中,实参类型其第二维必须为10,而第一维是多少无所谓。
    //以下三种形式等价
    void print(int array[][10]);
    void print(int (*array)[10]);
    void print(int array[5][10]);
    

三、main函数形参

在运行程序时,可以为main函数传入任意数量的参数,这需要将我们将main函数声明称以下任意一种形式:

int main(int argc, char *argv[]) { ... }
int main(int argc, char **argv) { ... }

其中,argv是一个C风格的字符串数组,argv[0]默认为程序路径名,argc是除argv[0]外剩余参数的数量,可以借助argc访问该参数数组。

// main.exe arg1 arg2
#include <iostream>
int main(int argc, char *argv[]) { 
	std::cout << argv[1] << "\n" << argv[2];
}
// stdout:
// arg1
// arg2

四、可变形参

C++实现长度可变的形参列表有三种形式(C++ 11):

  1. initializer_list
    initializer_list是一种标准库模版,其使用方式与vector容器比较类似,将该模版实例化类型作为形参,可以传入任意数量相同类型的参数。该类型支持列表初始化、size获取参数包大小以及通过begin()end()获取首尾迭代器。
    void func(initializer_list<int> args){
    	for(int arg: args){
    		// 元素操作(注意无法使用int &,
    		// 因为args中的元素均为const int类型)
    	}
    }
    
    使用时需要注意几点:
    • initializer_list所有参数均为常数类型,函数中无法对参数包中的参数进行修改。
    • 使用拷贝或赋值操作时,该参数包中的元素不会发生拷贝,原参数包与目标参数包将共享同一元素副本。
  2. C兼容的...形参
    不熟悉这个,以后填坑,详情可参照cpprefrence链接。
  3. 变参模版
    不熟悉这个,以后填坑,详情可参照cpprefrence链接。

五、默认实参

  1. 默认实参的值不属于函数原型的一部分,因此无法通过修改默认实参来实现函数重载。如果相同的函数原型声明中修改了默认实参的值,则会报“默认实参重定义”的错误。

    int test(int arg1, int arg2=10);
    int test(int arg2, int arg2=12);
    // error: redefinition of default argument
    
  2. 默认实参的名字需要在其作用域内可见,可以是符合类型要求的任意表达式,该表达式的求值在函数调用时发生。(这也就是说,我们有机会在函数调用前更改一个函数的默认实参

    int todayT = 10;
    
    // 以函数作为默认实参名称,将在调用时进行求值
    int test(int tempreture = todayT){
    	return tempreture;
    }
    
    cout << test(); // 10
    todayT = 15; // 改变默认实参的返回值
    cout << test(); // 15
    

六、形参会忽略实参的顶层const

void test(int arg){
    arg = 10;
}
const int i = 15;
test(i);

上面的函数将成功调用。此时发生的是对常数变量值得拷贝,因此顶层const属性将被忽略,无论形参类型是否具有const均可。但若实参具有底层const,则形参必须为const。

七、C风格字符串形参

void test(char *a); // 无法传入字面值
void test(const char *a); // 可传入字面值
void test(const char a[10]); // 与第二种等价

在编写程序时,经常会有需要向函数中传递一个字面值C风格字符串,例如"abcde",像这个有五个有效字符的字面值,其类型为const char[6](维度考虑结尾空字符)。因此第一种函数并不适用。
考虑之前所提,对于数组指针形参而言,维度并无实际意义,因此第三种与第二种函数声明登记,"abcde"可以合法传入第三个函数中。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ Primer(第五版)》是一本深入浅出、权威全面的C++教程,并且非常适合初学者。这本书以系统的方式介绍了C++语言的基础知识,包括语法、数据类型、控制结构等。通过大量的实例和练习,读者能够更好地理解C++的概念和应用。 这本书的第五版是经过完全修订和更新的,以跟上C++11、C++14和C++17的最新标准。它包含了对新特性的详细解释,例如自动类型推导、lambda表达式、增强的类型检查等等。同时,书中还介绍了C++标准库和常用的编程技巧,如字符串处理、容器、迭代器等,以帮助读者更好地应用C++语言进行程序开发。 《C++ Primer(第五版)》的特点之一是它的逻辑性和结构性。每个章节都按照逐步深入的方式呈现,从基本概念开始,然后逐步引入更高级的内容。每个概念都配有大量的例子和实践题目,读者可以通过反复实践来巩固和加深对知识的理解。 此外,这本书还有一个非常有用的附录和索引,方便读者查找和理解C++语言中的各种概念和特性。 总的来说,如果你想系统学习和掌握C++编程语言,我强烈推荐你阅读《C++ Primer(第五版)》。它的全面性和权威性能够帮助你建立牢固的编程基础,并且对于学习其他高级主题和应用也提供了很好的引导。无论你是初学者还是有一定编程基础的人,这本书都能够帮助你进一步提升自己的C++编程能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值