2011-02-14 112 views

回答

260

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

编辑:正如其他人所指出的,你可以的定义的说法,但我会建议所有写代码,如果这是不正确的。

84

你可以做,但从来都没有。通常你在函数声明中做,然后所有的调用者都可以使用这个默认值。但you can do that at function definition instead然后只有那些看到定义的人才能使用默认值。

+24

这可能是技术上是正确的,但我不认为这是个好建议。 – 2011-02-14 06:43:51

+0

@Marcelo Cantos:同意。 – sharptooth 2011-02-14 06:44:31

+28

如果你想变得非常可怕,你可以实际做到这一点,但对于不同的参数。 :-) – 2011-02-14 21:03:15

10

该声明通常是最“有用”的,但这取决于您想如何使用该类。

两者都无效。

73

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

有些人喜欢在实施过添加默认值(为注释):

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

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

但是,这意味着重复,将增加其与代码不同步的注释的可能性(还有什么比未注释的代码更糟糕?)。

10

好问题... 我发现编码器通常使用声明来声明默认值。我一直在举行一个方法(或警告)或其他过基于编译器

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

如果此功能被暴露 - 非成员,公共或受保护的 - 那么调用者应该知道他们,默认值必须位于标题中。

如果这些函数是私有的并且是非线性的,那么将默认值放在实现文件中是有意义的,因为这允许不会触发客户端重新编译的更改(对于低级库在企业规模发展中共享)。也就是说,这肯定会让人感到困惑,并且在头文件中以更直观的方式呈现API时存在文档价值,所以选择妥协 - 尽管在没有任何令人信服的理由的情况下,一致性是主要的。

35

虽然这是一个“老”的线程,我还是想添加以下到它:

我已经经历了下情况:

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

正如人们所看到的,我已经把类源文件中的参数“力”的默认值,而不是在类的头文件。

然后我用该函数在派生类如下(派生类继承在公开的方式的基类):

SetI2cSlaveAddress(addr);

假定它会采取“力”参数为“假”“的理所当然”。

然而,编译器(放于C++ 11模式)抱怨,给了我以下编译器错误:

/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)

那么编译器很高兴,所有代码和预期一样(我可以给一个或两个参数的函数SetI2cSlaveAddress())!

因此,不仅对一个类的用户来说,将一个参数的默认值放在头文件中是很重要的,这同样也是编译和功能上的明智之举,显然它似乎是必须的!

3

您可以按照标准来做,但请记住,如果您的代码在包含默认参数的定义之前看到没有默认参数的声明,则可能出现编译错误。例如,如果包含包含没有默认参数列表的函数声明的头文件,编译器会查找该原型文件,因为它不知道默认参数值,因此原型不匹配。

如果你在定义中使用默认参数的函数,那么包含该文件,但我不会建议。

3

添加一个点。具有默认参数的函数声明应该是从右到左排列的从上到下

例如在下面的函数声明中,如果您更改声明顺序,那么编译器会给您一个缺少的默认参数错误。编译器允许您在相同范围内将函数声明与默认参数分开,但应该按照从RIGHT到LEFT(默认参数)以及从TOP到BOTTOM(函数声明默认参数的顺序)的顺序。

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

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

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

它取决于您要调用哪个值的接口。

例如在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

我极力劝阻你使用这样