实例讲解在C++的函数中变量参数及默认参数的使用
包含变量参数列表的函数
如果函数声明中最后一个成员是省略号 (...),则函数声明可采用数量可变的参数。在这些情况下,C++ 只为显式声明的参数提供类型检查。即使参数的数量和类型是可变的,在需要使函数泛化时也可使用变量参数列表。函数的系列是一个使用变量参数列表的函数的示例。printfargument-declaration-list
包含变量参数的函数
若要访问声明后的参数,请使用包含在标准包含文件 STDARG.H 中的宏(如下所述)。
采用数量可变的参数的函数声明至少需要一个占位符参数(即使不使用它)。如果未提供此占位符参数,则无法访问其余参数。
当 char 类型的参数作为变量参数进行传递时,它们将被转换为 int 类型。同样,当 float 类型的参数作为变量参数进行传递时,它们将被转换为 double 类型。其他类型的参数受常见整型和浮点型提升的限制。
使用参数列表中的省略号 (...) 来声明需要变量列表的函数。使用在 STDARG.H 包含文件中描述的类型与宏来访问变量列表所传递的参数。有关这些宏的详细信息,请参阅 va_arg、va_copy、va_end、va_start。(处于 C 运行时库文档中)。
以下示例演示如何将宏与类型一起使用(在 STDARG.H 中声明):va_listva_endva_argva_start
// variable_argument_lists.cpp #include <stdio.h> #include <stdarg.h> // Declaration, but not definition, of ShowVar. void ShowVar( char *szTypes, ... ); int main() { ShowVar( "fcsi", 32.4f, 'a', "Test string", 4 ); } // ShowVar takes a format string of the form // "ifcs", where each character specifies the // type of the argument in that position. // // i = int // f = float // c = char // s = string (char *) // // Following the format specification is a variable // list of arguments. Each argument corresponds to // a format character in the format string to which // the szTypes parameter points void ShowVar( char *szTypes, ... ) { va_list vl; int i; // szTypes is the last argument specified; you must access // all others using the variable-argument macros. va_start( vl, szTypes ); // Step through the list. for( i = 0; szTypes[i] != '\0'; ++i ) { union Printable_t { int i; float f; char c; char *s; } Printable; switch( szTypes[i] ) { // Type to expect. case 'i': Printable.i = va_arg( vl, int ); printf_s( "%i\n", Printable.i ); break; case 'f': Printable.f = va_arg( vl, double ); printf_s( "%f\n", Printable.f ); break; case 'c': Printable.c = va_arg( vl, char ); printf_s( "%c\n", Printable.c ); break; case 's': Printable.s = va_arg( vl, char * ); printf_s( "%s\n", Printable.s ); break; default: break; } } va_end( vl ); } //Output: // 32.400002 // a // Test string
上一个示例演示以下重要概念:
在访问任何变量参数前,必须建立一个列表标记作为类型 va_list 的变量。在前面的示例中,该标记称为 vl。
使用 va_arg 宏访问各个参数。必须告知 va_arg 宏要检索的参数的类型,以便它可以从堆栈中传输正确的字节数。如果为 va_arg 指定的大小的类型与通过调用程序提供的类型不同,则结果是不可预知的。
应将使用 va_arg 宏获取的结果显式强制转换为所需类型。
必须调用宏以终止可变参数处理。va_end
默认参数
在许多情况下,函数具有不常使用的参数,因为使用默认值便已足够。为了解决此问题,默认参数工具允许为函数仅指定在给定调用中有意义的参数。为了阐释此概念,请考虑函数重载中所示的示例。
// Prototype three print functions. int print( char *s ); // Print a string. int print( double dvalue ); // Print a double. int print( double dvalue, int prec ); // Print a double with a // given precision.
在许多应用程序中,可为 prec 提供合理的默认值,从而消除对两个函数的需求:
// Prototype two print functions. int print( char *s ); // Print a string. int print( double dvalue, int prec=2 ); // Print a double with a // given precision.
略微更改了 print 函数的实现以反映类型 double 仅存在一个此类函数这一事实:
// default_arguments.cpp // compile with: /EHsc /c // Print a double in specified precision. // Positive numbers for precision indicate how many digits // precision after the decimal point to show. Negative // numbers for precision indicate where to round the number // to the left of the decimal point. #include <iostream> #include <math.h> using namespace std; int print( double dvalue, int prec ) { // Use table-lookup for rounding/truncation. static const double rgPow10[] = { 10E-7, 10E-6, 10E-5, 10E-4, 10E-3, 10E-2, 10E-1, 10E0, 10E1, 10E2, 10E3, 10E4, 10E5, 10E6 }; const int iPowZero = 6; // If precision out of range, just print the number. if( prec >= -6 && prec <= 7 ) // Scale, truncate, then rescale. dvalue = floor( dvalue / rgPow10[iPowZero - prec] ) * rgPow10[iPowZero - prec]; cout << dvalue << endl; return cout.good(); }
若要调用新的 print 函数,请使用如下代码:
print( d ); // Precision of 2 supplied by default argument. print( d, 0 ); // Override default argument to achieve other // results.
使用默认参数时,请注意以下几点:
默认参数仅在其中省略了尾随参数的函数调用中使用 - 它们必须是最后的参数。因此,以下代码是非法的:
int print( double dvalue = 0.0, int prec );
默认参数不能在以后的声明中重新定义,即使重新定义的参数与原始参数相同也是如此。因此,以下代码将生成错误:
// Prototype for print function. int print( double dvalue, int prec = 2 ); ... // Definition for print function. int print( double dvalue, int prec = 2 ) { ... }
此代码的问题在于定义中的函数声明重新定义了 prec 的默认参数。
以后的声明可添加额外的默认参数。
可为指向函数的指针提供默认参数。例如:
int (*pShowIntVal)( int i = 0 );