【问题标题】:Is it OK for an abstract base class have non-abstract methods?抽象基类有非抽象方法可以吗?
【发布时间】:2009-12-01 20:45:39
【问题描述】:

一个抽象基类(接口类)通常把它的所有成员函数都抽象化。但是,我有几种使用由调用接口抽象方法组成的成员函数的情况。我可以在派生但仍然抽象的类中实现它们,或者我可以将方法实现为接口类的非抽象、非虚拟方法。

在实现接口类中的方法时是否存在设计方面的问题?是不是风格不好,如果是,为什么?

静态方法也一样吗?

例如

class IFoo
{
    public:
    virtual ~IFoo();
    virtual double calcThis( InputType p ) const = 0;
    virtual double calcThat( InputType p ) const = 0;
    double calcFraction( InputType p ) { return calcThis( p ) / calcThat( p ); }
    static BarType bar( InputType p );
};

class MyFoo : public IFoo
{
    public:
    // implements IFoo
    virtual double calcThis( InputType p ) const;
    // implements IFoo
    virtual double calcThat( InputType p ) const;
};

class IFoo
{
    public:
    virtual ~IFoo();
    virtual double calcThis( InputType p ) const = 0;
    virtual double calcThat( InputType p ) const = 0;
};

class FooBase : public IFoo
{
    public:
    virtual ~FooBase();
    double calcFraction( InputType p ) { return calcThis( p ) / calcThat( p ); }
    static BarType bar( InputType p );
};

class MyFoo : public FooBase
{
    public:
    // implements IFoo
    virtual double calcThis( InputType p ) const;
    // implements IFoo
    virtual double calcThat( InputType p ) const;
};

【问题讨论】:

    标签: c++ coding-style


    【解决方案1】:

    当然。这就是template method pattern 的工作方式(尽管这不一定是实现接口)——这是完全可以接受的,而且通常是个好主意。

    Java AbstractList 类就是一个很好的例子。

    编辑:很抱歉之前没有回复。您的calcFraction 方法在我看来就像是模板方法模式的实现——它是基于调用抽象方法的具体实现。

    现在我同意 DrPizza 的观点——如果你将某个东西称为接口,它真的不应该有任何实现。抽象基类可以具有非抽象方法(根据您的问题标题),但我不认为“抽象基类”和“接口类”是等价的。这可能是我的 C# 和 Java 背景,您可以在其中将类型声明为 as 接口 - 但无法提供任何实现。

    【讨论】:

    • 有趣的一点,但是模板方法(或策略模式,或非虚拟接口)恰恰相反,不是吗?那将是一个面向实现的类,很可能包含数据成员和所有数据成员,它将其部分实现委托给派生类。
    • 确实,抽象基础中的具体函数非常好。在这种情况下我正在考虑sutter的文章:gotw.ca/publications/mill18.htm
    【解决方案2】:

    如果您将其称为接口(即您似乎使用命名约定“IFoo”),那么它应该是一个纯接口(没有实现)。

    如果它只是一个抽象类,那么纯虚方法和实现方法的混合是完全合理的。

    【讨论】:

    • 您能否更具体/详细说明为什么我应该将其保留为纯界面?这仅仅是惯例还是有其他处罚?
    • 按照惯例,标记为接口的东西是“纯”的。在某些情况下,我怀疑您的接口必须是纯的,例如 COM,但如果您只是在编写 C++,这完全取决于您。在编写接口时,是否有对每个实现者有用的通用功能?如果是这样,则将其设为抽象类。如果不是——如果每个实现都有很大的不同——那就选择一个纯接口。
    • 请记住,这纯粹是关于您如何称呼它的问题:“接口”通常仅表示“没有函数实现的抽象类”。一种选择是将您的类拆分为一个仅具有纯虚函数(您称为“接口”)的基类,另一个是从中派生并实现其中一些(您称为“mixin”)的基类。 mixin 的作用是让具体类只实现少量的功能。但是您也可以允许具体类自己完成所有工作,而不是从 mixin 派生。
    • ... 这是因为从抽象类继承然后覆盖所有非纯函数以及纯函数,充其量是没有意义的,最坏的情况可能会误导某人或导致错误,如果有人假设一个函数的所有实现都具有某些特定的性能特征,而基类实现恰好具有。
    【解决方案3】:

    绝对是的。这是使用抽象类而不是接口的原因之一。

    【讨论】:

      【解决方案4】:

      我一直认为这就是抽象类的实际用途:实现所有后代共有的一些基本行为,但将特定于派生类的部分抽象化。

      【讨论】:

        【解决方案5】:

        是的。在抽象基类中实现方法(虚拟或非虚拟)非常好,并且通常是可取的。如果您真的想要一个接口,最好使用您的第二个构造,以便读者清楚使用,但两者都是完全合法的 C++。如果存在实现,我就不会使用“I”指示符。

        【讨论】:

          【解决方案6】:

          我看不出有什么理由不在抽象类中实现。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-03-12
            • 2012-09-16
            • 2010-11-20
            • 1970-01-01
            • 2012-09-21
            • 1970-01-01
            • 2016-12-05
            • 1970-01-01
            相关资源
            最近更新 更多