抽象基类是否可以拥有非抽象方法?

3

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

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

静态方法是否也适用于此?

例如:

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;
};
6个回答

9
当然可以。这就是模板方法模式的工作方式(尽管这并不一定是实现接口),这是完全可接受的,而且通常是一个好主意。
Java中AbstractList类就是一个很好的例子。
编辑:抱歉没有及时回复。在我看来,你的calcFraction方法就像是模板方法模式的实现 - 它是基于调用抽象方法的具体实现。
现在我同意DrPizza的观点 - 如果你把某个东西称为接口,它真的不应该有任何实现。对于一个抽象基类来说,有非抽象方法是可以的(与您的问题标题相符),但是我不认为“抽象基类”和“接口类”是等价的。这可能与我的C#和Java背景有关,在那里您可以将类型声明为接口 - 但是不能提供任何实现。

有趣的观点,但模板方法(或策略模式,或非虚拟接口)不是相反的吗?这将是一个面向实现的类,很可能具有数据成员和所有内容,它将一些实现委托给派生类。 - andreas buykx
实际上,在抽象基类中使用具体函数是完全可以的。我在这种情况下想到了Sutter的文章:http://www.gotw.ca/publications/mill18.htm。 - Johannes Schaub - litb

3
如果你把它称为接口(即通过使用命名约定“IFoo”似乎是这样),那么它应该是一个纯接口(没有实现)。
如果它只是一个抽象类,那么混合使用纯虚拟和已实现的方法是完全合理的。

你能否更具体、详细地说明为什么我应该保持一个纯接口?这仅仅是惯例还是有其他的惩罚吗? - andreas buykx
按照惯例,标记为接口的东西是“纯净的”。有一些情况下,我怀疑你的接口必须是纯净的,比如COM,但如果你只是写C++,那就由你自己决定了。在编写接口时,是否存在对每个实现者都有用的通用功能?如果是这样,那么将其制作成抽象类。如果没有 - 如果每个实现都会有很大不同 - 那就选择纯接口。 - DrPizza
1
请记住这纯粹是你称呼它的问题:“接口”通常只意味着“没有函数实现的抽象类”。一种选择是将您的类拆分为一个基类,其中仅有纯虚函数(您称之为“接口”),以及另一个类从该基类派生并实现其中一些函数(您称之为“mixin”)。mixin 的作用是允许具体类仅实现少量函数。但是您也可以允许具体类自己完成所有工作,并且根本不从 mixin 派生。 - Steve Jessop
1
这是因为继承自抽象类,然后覆盖所有非纯函数以及纯函数,最好的情况下是毫无意义的,最坏的情况下可能会误导某人或导致错误,如果有人假设所有函数的实现都具有某些特定的性能特征,而基类实现恰好具有这些特征。 - Steve Jessop

2

是的,绝对没错。这就是为什么要使用抽象类而不是接口的原因之一。


1
我一直认为抽象类的作用就是实现所有子类共有的基本行为,但将那些特定于派生类的部分定义为抽象。

0

是的。在抽象基类中实现方法(虚拟或非虚拟)是完全可以的,而且通常也是可取的。如果你真的想要一个接口,最好使用第二种构造方式,这样读者就能清楚地理解其用途,但两种方式都是合法的C++语法。只是如果有实现存在,我不会使用'I'标识符。


0
我看不出没有在抽象类中进行一些实现的理由。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接