2012-08-27 34 views
18

我创建了一个抽象基类,它具有带默认参数的纯虚拟方法。最佳做法:纯虚拟方法的缺省参数

class Base { 
    ... 
    virtual someMethod(const SomeStruct& t = 0) = 0; 
    ... 
} 

class Derived : public Base { 
    ... 
    virtual someMethod(const SomeStruct& t = 0); 
    ... 
} 

所以我想知道它是一个很好的做法,默认参数设置为纯虚和整体虚拟方法呢?

+3

我想你的意思是const SomeStruct * t = 0? – marcinj

+4

@luskan:'SomeStruct'可以隐式地从'0'转换。 –

+0

“将默认参数设置为纯虚拟”意味着什么? –

回答

13

我常常希望像你一样同时使用默认参数和虚拟功能。然而其他人却正确地指出,这导致含糊不清,通常不是一个好主意。有一个相当简单的解决方案,我使用。给你的虚拟函数一个不同的名字,使它受到保护,然后用默认的参数提供一个公用函数来调用它。

class Base { 
protected: 
    virtual void vSomeMethod(const SomeStruct& t) = 0; 
public: 
    void someMethod(const SomeStruc& t = 0) 
    { vSomeMethod(t); } 
} 

派生类只需覆盖vSomeMethod而且不用担心在所有关于默认参数。

+4

与其引入另一个名称“vSomeMethod”,为什么不使用重载?即添加一个公共的非虚拟成员函数'void someMethod()'到调用'someMethod(0);'的'Base'。在更普遍的情况下,你需要一个虚拟成员函数'f(xn,...,x1,x0)',它可以用'n + 1'默认参数值调用,'xn = vn,...,x1 = v1,x0 = v0','f'的虚拟过载可能没有默认值,并且可能会有'n'默认参数'f(xn = vn,...,x1 = v1) '调用'f(xn,...,x1,v0)'。 – Ose

+2

@Ose使用单独命名的实现函数而不是重载相同名称的原因是,当您覆盖子类中的虚拟实现时,将隐藏非父虚拟助手,要求您另外使用该方法进入孩子。 –

6

都尽量不要使用默认参数,但如果这样做,从来不会重新定义它们(see the text for details

都买了有效的C++的书斯科特迈尔斯。你不会后悔的。

+0

我在这两种方法中设置了相同的默认值,我认为您的引用是关于不同值可以设置为默认参数时的ambiguouse情况。 – deimus

+0

deimus,真的没关系。即使你_think_你将总是使用与默认参数相同的值,那么犯一个错字或者忘记提供一个默认参数是非常容易的。调试这种错误将使你的生活真正悲惨与任何适当的代码库。 –

+2

@deimus你不能保证将来有人不会继承你的基类并改变默认值。 – juanchopanza

27

实际上,您的代码是默认参数中可能最差的使用模式之一,因为它涉及继承和多态行为。我支持建议看看相关的Scott Meyers提示,但这里有一个简短的概述:

在多态调用的情况下,根据静态类型声明使用默认参数,而不是动态类型。这是合乎逻辑的,因为运行时间不知道默认参数,但是打破了关于多态行为的任何理智假设。例如,

#include <cstdio> 

class Base 
{ 
     public: 
       virtual void f(int a = 1) 
       { 
         printf("Base::f(%d)\n", a); 
       } 
}; 

class Deriv : public Base 
{ 
     public: 
       virtual void f(int a = 2) 
       { 
         printf("Deriv::f(%d)\n", a); 
       } 
}; 

int main() 
{ 
     Base* a = new Deriv(); 
     a->f(); 
     delete a; 
     return 0; 
} 

产量:

Deriv::f(1) 
+0

很好的例子,显示了与这种方法相关的陷阱。 –

+1

您的示例让我大惑不解,+1 – asimes

0

我将:

  • 定义与参数的虚拟功能(没有默认值)
  • 在 所有定义的基类非虚函数,不带参数,调用传递的虚拟功能正确的默认参数

这样派生类根本就不用关心默认值。

0

如果你想这个代码意义:

Base* p = new Derived; 
p->someMethod(); 

因为静态类型的pBase*这是在调用中使用的基本特征。 分配了默认值,并且作为虚函数,呼叫被重定向到派生。

你甚至可以让他们不同的定义,如果你想你的派生::的someMethod从Base*而不是Derived*收到不同的值...

重要的是这些文件“关系”好了,既然大多数程序员通过简单阅读代码就无法理解它们。

当然,如果所有这些都是不适合你特定情况下,比其他产生更多的混乱,避免虚函数的默认参数,并使用辅助非虚拟一个正确地调用它们。

但也考虑到-by读者standpoint-默认参数大于过载函数调用私下另一个具有不可读取参数返工更explicative。