Where to put default parameter value in C++?
默认参数值的位置是什么?只是在函数定义、声明或两者中?
默认参数值必须出现在声明中,因为这是调用方看到的唯一内容。
编辑:正如其他人所指出的,您可以对定义进行论证,但我建议您编写所有代码,就好像这不是真的一样。
你也可以这样做,但不能两者兼而有之。通常在函数声明中执行,然后所有调用方都可以使用该默认值。但是,您可以在函数定义中这样做,然后只有那些看到定义的人才能使用默认值。
最有用的地方是声明(.h),以便所有用户都能看到它。
有些人也喜欢在实现中添加默认值(作为注释):
1 2 3 4 5 6 7 8 | void foo(int x = 42, int y = 21); void foo(int x /* = 42 */, int y /* = 21 */) { ... } |
但是,这意味着重复,并且会增加注释与代码不同步的可能性(比未注释的代码更糟糕的是什么?带有误导性注释的代码!).
虽然这是一个"旧"线程,但我还是想在其中添加以下内容:
我经历过下一个案例:
- 在一个类的头文件中,我
1 int SetI2cSlaveAddress( UCHAR addr, bool force );
- 在那个类的源文件中,我
1
2
3
4 int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force = false )
{
...
}
如您所见,我已经将参数"force"的默认值放在类源文件中,而不是放在类头文件中。
然后,我在派生类中使用该函数,如下所示(派生类以公共方式继承了基类):
SetI2cSlaveAddress( addr );
假设它将"force"参数视为"false",这是理所当然的。
但是,编译器(放入C++ 11模式)抱怨并给了我以下编译器错误:
1 2 3 4 5 6 7 8 9 10 | /home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp: In member function 'void CMax6956Io::Init(unsigned char, unsigned char, unsigned int)': /home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: error: no matching function for call to 'CMax6956Io::SetI2cSlaveAddress(unsigned char&)' /home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: note: candidate is: In file included from /home/geertvc/mystuff/domoproject/lib/i2cdevs/../../include/i2cdevs/max6956io.h:35:0, from /home/geertvc/mystuff/domoproject/lib/i2cdevs/max6956io.cpp:1: /home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: int CI2cHal::SetI2cSlaveAddress(unsigned char, bool) /home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: candidate expects 2 arguments, 1 provided make[2]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/max6956io.cpp.o] Error 1 make[1]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/all] Error 2 make: *** [all] Error 2 |
但当我在基类的头文件中添加默认参数时:
int SetI2cSlaveAddress( UCHAR addr, bool force = false );
并将其从基类的源文件中删除:
int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force )
然后编译器很高兴,所有代码都按预期工作(我可以给函数
因此,不仅对于类的用户来说,在头文件中放入参数的默认值是很重要的,而且在编译和功能方面,显然这是必须的!
如果函数是公开的(非成员的、公共的或受保护的),那么调用者应该知道它们,并且缺省值必须在头中。
如果函数是私有的,并且不符合标准,那么将默认值放在实现文件中是有意义的,因为这允许不触发客户端重新编译的更改(对于企业级开发中共享的低级库,这有时是一个严重的问题)。这就是说,它确实有潜在的混乱,并且在头部以更直观的方式呈现API有文档价值,所以选择折衷的方法——尽管在没有任何令人信服的原因的情况下,一致性是最主要的。
好问题…我发现编码人员通常使用声明来声明默认值。我一直被一种方式(或警告)或另一种方式基于编译器
1 2 3 4 5 6 | void testFunct(int nVal1, int nVal2=500); void testFunct(int nVal1, int nVal2) { using namespace std; cout << nVal1 << << nVal2 << endl; } |
声明通常是最"有用"的,但这取决于您希望如何使用类。
两者都无效。
还有一点我没发现有人提到:
如果您有虚拟方法,每个声明都可以有自己的默认值!
这取决于您调用的接口将使用哪个值。
IDeone示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | struct iface { virtual void test(int a = 0) { std::cout << a; } }; struct impl : public iface { virtual void test(int a = 5) override { std::cout << a; } }; int main() { impl d; d.test(); iface* a = &d; a->test(); } |
它打印
我强烈反对你这样使用它
再加一点。带有默认参数的函数声明应该从右向左和从上到下排序。
例如,在下面的函数声明中,如果您更改了声明顺序,那么编译器会给出一个缺少的默认参数错误。原因编译器允许您在同一范围内用默认参数分隔函数声明,但它应该从右到左(默认参数)和从上到下(函数声明默认参数的顺序)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //declaration void function(char const *msg, bool three, bool two, bool one = false); void function(char const *msg, bool three = true, bool two, bool one); // Error void function(char const *msg, bool three, bool two = true, bool one); // OK //void function(char const *msg, bool three = true, bool two, bool one); // OK int main() { function("Using only one Default Argument", false, true); function("Using Two Default Arguments", false); function("Using Three Default Arguments"); return 0; } //definition void function(char const *msg, bool three, bool two, bool one ) { std::cout<<msg<<""<<three<<""<<two<<""<<one<<std::endl; } |
您可以这样做(根据标准),但请记住,如果您的代码在包含默认参数的定义之前看到没有默认参数的声明,则可能会出现编译错误。
例如,如果您包含包含没有默认参数列表的函数声明的头,那么编译器将查找该原型,因为它不知道您的默认参数值,因此原型将不匹配。
如果要在定义中放入带有默认参数的函数,请包含该文件,但我不建议这样做。