C++ 良好实践:纯虚拟方法的默认参数

C++ 良好实践:纯虚拟方法的默认参数,c++,C++,我创建了一个抽象基类,它有一个带有默认参数的纯虚方法 class Base { ... virtual someMethod(const SomeStruct& t = 0) = 0; ... } class Derived : public Base { ... virtual someMethod(const SomeStruct& t = 0); ... } 所以我想知道,将默认参数设置为纯虚方法,将全局参数设置为虚方法,

我创建了一个抽象基类,它有一个带有默认参数的纯虚方法

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

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

所以我想知道,将默认参数设置为纯虚方法,将全局参数设置为虚方法,这是一种好的做法吗

如果可能的话,根本不要使用默认参数,但如果使用,就永远不要重新定义它们()


< Scott Meyers >购买两本有效的C++书籍。您不会后悔的。

事实上,您的代码是默认参数最糟糕的使用模式之一,因为它涉及继承和多态行为。我支持查看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;
}

我经常希望像您一样使用默认参数和虚拟函数。然而,其他人正确地指出,这会导致歧义,通常不是一个好主意。有一个相当简单的解决方案,我使用的。为虚拟函数指定一个不同的名称,使其受保护,然后提供一个具有调用它的默认参数的公共函数

class Base {
protected:
    virtual void vSomeMethod(const SomeStruct& t ) = 0;
public:
    void someMethod( const SomeStruc& t = 0 )
    { vSomeMethod( t ); }
}
派生类只需重写
vSomeMethod
,而不必担心默认参数。

我会:

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

这样,派生类就不必关心默认值。

如果您希望此代码有意义:

Base* p = new Derived;
p->someMethod();
由于
p
的静态类型是
Base*
,因此在调用时使用的是基本签名。 默认值被分配,并且作为虚函数,调用被重定向到派生

如果希望派生::someMethod从
Base*
接收不同的值,而不是从
Derived*
接收不同的值,您甚至可以对它们进行不同的定义

重要的是要很好地记录这些“关系”,因为大多数程序员不会从简单的代码阅读中理解它们

当然,如果所有这些都不适合您特定的上下文,会产生更多的混乱,请避免虚拟函数上的默认参数,并使用非虚拟辅助函数正确调用它们


但同时考虑到,从读者的角度来看,默认参数比重载函数用不可读的参数重写私下调用另一个更为明确。

我想你是指const SomeStruct*t=0?@luskan:
SomeStruct
可以从
0
隐式转换。你说的是什么“将默认参数设置为纯虚拟“?我的派生类someMethod应该有默认参数,它是从基类实现纯虚拟接口。通过将默认参数设置为纯虚拟方法,我的意思就是我在示例中所写的。我在两种方法中都设置了相同的默认值,我认为您的参考是关于当不同的值可以设置为默认参数时的模糊情况。deimus,这其实并不重要。即使您认为将始终使用与默认参数相同的值,也很容易输入错误或忘记提及默认参数。调试这种错误会让你的生活变得非常痛苦,因为你不能保证将来不会有人继承你的基类并更改默认值。与其引入另一个名称“vSomeMethod”,为什么不使用重载呢?即,将一个公共非虚拟成员函数
void somethod()
添加到调用
somethod(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使用单独命名的实现函数而不是重载相同名称的原因是,当您重写子类中的虚拟实现时,您将非虚拟帮助器隐藏在父类中,这要求您在子类中另外使用该方法。这是一个很好的例子,说明了该方法的缺陷但问题是关于基类中的纯虚方法。
Base* p = new Derived;
p->someMethod();