没有抽象方法的抽象类

Posted

技术标签:

【中文标题】没有抽象方法的抽象类【英文标题】:Abstract class without abstract methods 【发布时间】:2011-07-10 16:21:47 【问题描述】:

是否可以在不声明任何抽象方法的情况下在 C++ 中创建类抽象?目前,我有一个带有 StaticSprite 和 DynamicSprite 子类的 Sprite 类。我想让 Sprite 类抽象化。

问题是他们没有共享任何方法。好吧,StaticSprite 和 DynamicSprite 可能共享一个 draw() 方法,但是这个方法的参数不同,所以这不是一个选项。

谢谢!

编辑: 这是演示我正在尝试做的代码:

精灵:

class Sprite

    public:
        Sprite(HINSTANCE hAppInst, int imageID, int maskID);
        ~Sprite();

    protected:
        HINSTANCE hAppInst;
        HBITMAP hImage;
        HBITMAP hMask;
        BITMAP imageBM;
        BITMAP maskBM;
        HDC hSpriteDC;
;

静态精灵:

class StaticSprite : public Sprite

    public:
        StaticSprite(HINSTANCE hAppInst, int imageID, int maskID);
        ~StaticSprite();

        void draw(Position* pos, HDC hBackbufferDC);
;

动态精灵:

class DynamicSprite : public Sprite

    public:
        DynamicSprite(HINSTANCE hAppInst, int imageID, int maskID);
        ~DynamicSprite();

        void draw(HDC hBackbufferDC);
;

如您所见,创建一个 Sprite 对象是没有用的,所以我想将该类抽象化。但我不能使 draw() 抽象,因为它使用不同的参数。

【问题讨论】:

为什么要抽象没有抽象方法? 他们可能不希望它被实例化 拥有一个没有方法可继承的基类有什么意义? 因为那个类只有一些字段和一个构造函数/析构函数,但是类的行为应该由子类来实现。我知道这在 Java 中是可能的,所以我想知道 c++ 是否也支持这个。 我已经添加了我的代码来演示我正在尝试做的事情。 【参考方案1】:

你可以将你的析构函数声明为纯虚函数,因为所有类都有一个。

class AbstractClass

public:
    virtual ~AbstractClass() = 0 ;
 ;

但是,您需要在别处定义此析构函数。

AbstractClass::~AbstractClass() 

【讨论】:

由于基类析构函数是虚拟的,派生类析构函数的析构函数将在需要的时间和地点被调用。应该没有问题。 @Bv202 - 您还提供了析构函数的实现,因此您可以在那里进行清理。 @Bv202 你仍然可以实现一个纯虚析构函数来清理内存。 好吧,这可能是也可能不是 OP 想要的——它确实增加了类的开销并改变了语义。 如果它包含数据管理,它不是很抽象,是吗?另一种选择是使析构函数受到保护。你确实从中获得了那种力量,因为它不能单独被摧毁。【参考方案2】:

如果类什么都不做,什么也不提供,只是作为一个标记存在,没有理由担心它是否是抽象的。这基本上不是问题。

如果您绝对希望确保除了在继承上下文之外永远不会实例化,请使用protected constructors。

请避免制作纯虚析构函数。疯狂的谎言就是这样。

【讨论】:

【参考方案3】:

没有。

你当然可以用这个:

class Sprite

;

当然,当您尝试创建它的实例时,编译器不会抱怨。

你可以添加一个纯虚析构函数:

class Sprite

public:
    virtual ~Sprite () = 0;
;

或者你可以保护构造函数,停止实例化:

class Sprite

protected:
    Sprite ();
;

【讨论】:

【参考方案4】:

针对你帖子的具体情况,考虑私有继承。这样你就可以懒惰地使用基础的实现,而不会向世界宣传虚假的 IS-A 关系。

【讨论】:

【参考方案5】:

传统的做法是制作一个纯虚析构函数,然后实现它。

// .h
struct Foo 
  virtual ~Foo() = 0;
;

// .cpp
Foo::~Foo() 

【讨论】:

不能实现纯虚拟。 @Cthutu 其实你可以实现纯虚方法。这只是意味着孩子必须再次实现它。 在析构函数的情况下,你必须。 是的,你可以,参见例如10.4/2:“纯虚函数仅在使用限定 ID 语法 (5.1) 显式调用时才需要定义。”和 12.4/7:“析构函数可以声明为虚拟 (10.3) 或纯虚拟 (10.4);如果在程序中创建了该类或任何派生类的任何对象,则应定义析构函数。如果类具有基类具有虚拟析构函数的类,其析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。”【参考方案6】:

问题是没有 他们共享的方法。

嗯,你的问题就在那里。你不应该在这里使用继承!如果您不打算用一个班级代替另一个班级,那么他们就不应该处于父子关系中。

根据需要,它们应该是完全不相关的、模板或使用组合。

编辑:根据代码示例,您继承以重用,我建议改用组合。 Sprite 可以是 struct,由 DynamicSpriteStaticSprite 拥有。您可以根据需要将尽可能多/少的辅助逻辑放入Sprite

【讨论】:

我在第一篇文章中添加了代码。应该怎么解决? -1 因为你不可能知道是否应该使用继承。查看 Alexandrescu 的访问者框架,以找到与您的陈述形成鲜明对比的观点。【参考方案7】:

最好的方法是保护类的构造函数,这样类就不能被直接实例化,因此它本质上是抽象的:

class SomeBaseClass 

protected:
    SomeBaseClass() 
    SomeBaseClass(const SomeBaseClass &) 
    SomeBaseClass &operator=(const SomeBaseClass&) 
public:
    virtual ~SomeBaseClass() 
    ...
;

或者,更新的样式:

class SomeBaseClass 

protected:
    SomeBaseClass() = default;
    SomeBaseClass(const SomeBaseClass &) = delete;
    SomeBaseClass &operator=(const SomeBaseClass&) = delete;
public:
    virtual ~SomeBaseClass() = default;
    ...
;

析构函数通常最好保持公开,因为您可能希望通过其基类指针删除一个对象。

【讨论】:

【参考方案8】:

要创建一个不派生就无法实例化的纯抽象类 - 您必须至少定义一个抽象方法。

例如。

 virtual void blah() const = 0;

否则你可以使用任何类作为抽象类

【讨论】:

最终,问题是“为什么要打扰”? @Randolpho - 问题变了。你的意思是为什么要有一个抽象类,或者如果它没有方法为什么要抽象? 是的,问题变了。我讨厌这种情况发生。 :)【参考方案9】:

如果您确实必须使用继承,请考虑使用非虚拟接口 (NVI) 习惯用法(请参阅http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.3)

基本上,基类将定义一组重载的非虚拟方法,这些方法从派生类中调用受保护的纯虚函数。

你绝对不想做的是重载一个虚函数(看起来你真的很想做)。

【讨论】:

以上是关于没有抽象方法的抽象类的主要内容,如果未能解决你的问题,请参考以下文章

java抽象类和抽象方法

抽象类和抽象方法

抽象类和抽象方法

抽象类

Java抽象类

抽象类