在哪里把默认参数值在c++ ?

默认参数值的位置是什么?只是在函数定义或声明中,还是两个地方都有?

230359 次浏览

你可以任选其一,但不能两者兼得。通常在函数声明时这样做,然后所有调用者都可以使用该默认值。然而,你可以在函数定义中这样做和只有那些看到定义的人才能使用默认值。

默认参数值必须出现在声明中,因为这是调用者看到的唯一内容。

正如其他人指出的那样,你可以在定义上有参数,但我建议在编写所有代码时都认为这不是真的。

声明通常是最有用的,但这取决于你想如何使用这个类。

两者都是无效的。

c++将默认形参逻辑放在调用端,这意味着如果不能从调用位置计算默认值表达式,则不能使用默认值。

其他编译单元通常只包含声明,因此放置在定义中的默认值表达式只能在定义编译单元本身中使用(并且在定义之后,即在编译器看到默认值表达式之后)。

最有用的地方是在声明(.h)中,这样所有用户都能看到它。

有些人也喜欢在实现中添加默认值表达式(作为注释):

void foo(int x = 42,
int y = 21);


void foo(int x /* = 42 */,
int y /* = 21 */)
{
...
}

然而,这意味着重复,并将增加注释与代码不同步的可能性(没有注释的代码会更糟糕吗?带有误导性注释的代码!)。

< p >好问题…… 我发现编码器通常使用声明来声明默认值。我一直坚持一种方式(或警告)或其他太基于编译器

void testFunct(int nVal1, int nVal2=500);
void testFunct(int nVal1, int nVal2)
{
using namespace std;
cout << nVal1 << << nVal2 << endl;
}

如果函数是公开的——非成员、公共或受保护的——那么调用者应该知道它们,并且默认值必须在头文件中。

如果函数是私有的并且是外行的,那么将默认值放在实现文件中是有意义的,因为这样允许更改不会触发客户端重新编译(对于企业级开发中共享的低级库来说,这有时是一个严重的问题)。也就是说,这肯定会有潜在的混淆,并且在头文件中以更直观的方式呈现API具有文档价值,所以选择您的折衷方案——尽管当没有令人信服的理由时,一致性是主要的事情。

虽然这是一个“老”线程,我仍然想添加以下内容:

我经历了下一个案例:

  • 在类的头文件中,有
int SetI2cSlaveAddress( UCHAR addr, bool force );
  • 在那个类的源文件中,我有
int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force = false )
{
...
}

可以看到,我将参数“force”的默认值放在类源文件中,而不是类头文件中。

然后我在派生类中使用该函数,如下所示(派生类以公共方式继承基类):

SetI2cSlaveAddress( addr );

假设它将“force”参数视为“理所当然”的“false”。

然而,编译器(put in c++11 mode)抱怨并给出以下编译器错误:

/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

但是当我在基类的< em >头< / em >文件中添加默认参数时:

int SetI2cSlaveAddress( UCHAR addr, bool force = false );

并从基类的源文件中删除它:

int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force )

然后编译器很高兴,所有代码都按预期工作(我可以给函数SetI2cSlaveAddress()一个或两个参数)!

因此,不仅对于类的用户来说,将参数的默认值放在头文件中很重要,而且编译和功能方面显然是必须的!

你可以使用任何一种(根据标准),但请记住,如果你的代码在包含default参数的定义之前看到了没有default参数的声明,那么编译错误就会出现。

例如,如果你包含头包含函数声明没有默认参数列表,因此编译器将寻找原型,因为它不知道你的默认参数值,因此原型将不匹配。

如果你把函数和默认参数放在定义中,那么包括这个文件,但我不建议这样做。

再加一分。带有默认参数的函数声明应该是从右到左排序从上到下

例如在下面的函数声明中,如果你改变声明顺序,那么编译器会给你一个缺少默认参数的错误。编译器允许你在同一范围内将函数声明与默认参数分开,但它应该按照从右到左(默认参数)和从上到下(函数声明默认参数的顺序)的顺序进行。

//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;
}

还有一点我没有发现任何人提到过:

如果你有虚方法,每个声明都可以有自己的默认值!

这取决于您调用的接口将使用哪个值。

ideone上的示例

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();
}

它输出50

我强烈建议您这样使用它