关于函数传参的其他问题(const形参实参/可变形参)
const 形参和实参
当形参是 const 变量时,实参是 const 或者不是 const 变量都可以。 实参初始化形参时会忽略掉顶层 const:
1 void gel(const int a){ 2 ; 3 } 4 5 void gel(int a){ 6 ; 7 } 8 //这两个gel函数的形参列表是等价的,因此会出现编译错误。
指针或引用形参与 const
1 #include <iostream> 2 using namespace std; 3 4 void gel(int *a){ 5 ; 6 } 7 8 void gel(int &a){ 9 ; 10 } 11 12 int main(void){ 13 int i = 0; 14 const int ci = i; 15 string::size_type ctr = 0; 16 gel(&i);//调用形参是int*的gel函数 17 // gel(&ci);//错误,不能忽略底层const,即不能用指向const int的指针初始化int* 18 gel(i);//调用形参是int&类型的gel函数 19 // gel(ci);//错误,不能把普通引用绑定到const对象ci上 20 // gel(1024);//错误,非常量引用不能绑定字面值 21 // gel(ctr);//错误,类型不匹配,ctr是无符号类型的 22 return 0; 23 }
应该尽量使用常量引用避免将普通引用形参绑定到const 对象上的错误。
main 函数传参:
1 #include <iostream> 2 using namespace std; 3 4 int main(int argc, char **argv){ 5 cout << argc << endl; 6 for(int i = 0; i < argc; i++){ 7 cout << argv[i] << endl; 8 } 9 return 0; 10 }
main 函数有两个形参,通常命名为 argc 和 argv,其中 argv 是一个字符串数组,而 argc 是字符串数组的长度。其中 argv[0] 保存该源文件生成的可执行文件的名字,使用 argv 中的实参时,可选实参时从 argv[1] 开始的。
含有可变形参的函数:
为了能编写能处理不同数量实参的函数,c++11 新标准提供了两种主要的方法:如果所有的实参类型相同,可以传递一个名为 initializer_list 的标准库类型,如果实参的类型不同,可以编写一种特殊的函数--可变参数模板。
c++ 还有一种特殊的形参类型--省略符 可以传递可变数量的实参。这种功能一般只用于与 C 函数交互的接口程序。
initializer_list 定义在 initializer_list 头文件中。它提供的操作有:
1 initializer_list<T> lst;//默认初始化,T类型元素的空列表 2 initializer_list<T> lst{a, b, c...};//lst 的元素和初始值一样多,lst 的元素是对应初始值的副本,列表中的元素是 const 3 lst2(lst);//拷贝或赋值一个 initializer_list 对象不会拷贝列表中的元素,拷贝后,原始列表和副本共享元素 4 lst2 = lst; 5 lst.size();//列表中元素的数量 6 lst.begin();//返回指向列表中首元素的指针 7 lst.end();//返回lst列表中的尾后指针
和 vector 一样,initializer_list 也是一种模板类型,定义 initializer_list 对象时,必须说明列表中所含元素的类型。
1 initializer_list<string> ls;//initializer_list 的元素类型时 string 2 initializer_list<int> li;//initializer_list 的元素类型时 int
如果时向 initializer_list 形参中传递一个值的序列,则必须把序列放在一对花括号内,且允许多次调用传递的参数数目不同:
1 #include <iostream> 2 #include <initializer_list> 3 using namespace std; 4 5 void error_msg(initializer_list<string> li){ 6 for(auto indx : li){ 7 cout << indx << " "; 8 } 9 cout << endl; 10 } 11 12 int main(void){ 13 string s1, s2; 14 cin >> s1 >> s2; 15 if(s1 != s2) error_msg({"functionX", s1, s2}); 16 else error_msg({"functionX", "okay"}); 17 return 0; 18 }
因为 initializer_list 包含 begin 和 end 成员,所以可以使用范围 for 循环。
对于含有 initializer_list 形参的函数,其形参列表是允许含有其他类型的形参的,而 initializer_list 形参中只能传指定类型的对象。
1 #include <iostream> 2 #include <initializer_list> 3 using namespace std; 4 5 void error_msg(initializer_list<string> li, int cnt){ 6 cout << cnt << ":" << endl; 7 for(auto indx : li){ 8 cout << indx << " "; 9 } 10 cout << endl; 11 } 12 13 int main(void){ 14 string s1, s2; 15 cin >> s1 >> s2; 16 if(s1 != s2) error_msg({"functionX", s1, s2}, 1); 17 else error_msg({"functionX", "okay"}, 2); 18 return 0; 19 }
省略符形参:
省略符形参是为了方便于 c++ 程序访问某些特殊的 c 代码而设置的,这些代码使用了名为 varargs 的 c 标准库功能。省略符形参应该仅仅用于 c 和 c++ 通用的类型,且大多数类型的对象在传递给省略符形参时都无法正确拷贝。
省略符形参只能出现在列表的最后一个位置,它的表现形式只有以下两种:
void gel(parm_list, ...);
void gel(...);
第一种形式指定了 gel 函数的部分形参类型,对应于这些形参的实参将会执行正确的类型检查。省略符形参所对应的实参类型无需类型检查。在第一种形式中,声明符后面的逗号是可选的。
1 #include <iostream> 2 #include <initializer_list> 3 using namespace std; 4 5 void gel1(string, ...){} 6 7 void gel2(string ...){}//省略string后面的逗号 8 9 void gel3(...){} 10 11 void gel3(int a, int b){ 12 cout << a << " " << b << endl; 13 } 14 15 int main(void){ 16 int a = 1, b = 2; 17 string s1 = "jf", s2 = "jfk"; 18 char ch1 = 'f', ch2 = 'j'; 19 gel1(s1, a, b, ch1, ch2); 20 // gel1(a, a, a);//错误,指定了的形参会进行类型检查 21 gel2(s1, a, b); 22 // gel3(s1, a, b);//错误,string不是c和c++通用的类型 23 gel3(a, b, ch1, ch2); 24 gel3(a, b);//输出1 2.省略号的优先级别最低,所以在函数解析时,只有当其它所有的函数都无法调用时,编译器才会考虑调用省略号函数的。 25 return 0; 26 }