开发者

Virtual deconstructors in interface->abstract->concrete class design

开发者 https://www.devze.com 2023-04-13 08:04 出处:网络
I have tried to answer this myself, by looking up several questions at StackOverflow. And although I think I understand this correctly, I can\'t fix this. Which, leaves me with the only obvious observ

I have tried to answer this myself, by looking up several questions at StackOverflow. And although I think I understand this correctly, I can't fix this. Which, leaves me with the only obvious observation: I still don't get it.

I have made a summary of the questions at the bottom of this post, everything in between is information I have gathered and context for this question.

So, I get it that when you have a base class, and a derived class, your deconstructor should be marked virtual in the base class. To allow polymorphism.

But, I cannot seem to get my code to compile, or when it does compile, it does not link due 'undefined references'. I have been changing ba开发者_开发百科ck and forth, but I never seem to get out of this cycle.

Basically I have an interace, defined like this:

#ifndef GUIELEMENT_H_
#define GUIELEMENT_H_

class GuiElement {
    public:
    virtual ~GuiElement();
    virtual void draw() = 0;
};

#endif /* GUIELEMENT_H_ */

I have several objects extending from this. A simple relation is GuiWindow (directly derives from GuiElement):

#ifndef CGUIWINDOW_H_
#define CGUIWINDOW_H_

#include <assert.h>
#include <cstddef>

#include "../GuiElement.h"
#include "../GuiInteractionDelegate.h"

class GuiWindow : public GuiElement {

    public:
        GuiWindow(GuiInteractionDelegate * guiInteractionDelegate) {
            assert(guiInteractionDelegate);
            interactionDelegate = guiInteractionDelegate;
        }

        ~GuiWindow() {
            //delete interactionDelegate;
        }

        // called each frame, delegates its behavior to the given concrete cGuiWindowDelegate class.
        void interact() {
            interactionDelegate->interact(this);
        }

    private:
        GuiInteractionDelegate * interactionDelegate;

};

#endif /* CGUIWINDOW_H_ */

This code does not link, gives me:

undefined reference to `GuiElement::~GuiElement()'

I thought it was sufficient to have an implementation in the GuiWindow class? Is that correct?

The next thing, which is really bugging me, is that I also have an abstract class derived from GuiElement, and concrete implementations on top of that. Basically giving: GuiElement->GuiShape->GuiButton

Here is the header of GuiShape:

#ifndef GUISHAPE_H_
#define GUISHAPE_H_

#include "../GuiElement.h"
#include "../../gameobjects/Rectangle.h"

class GuiShape : public GuiElement {

    public:
        GuiShape(Rectangle * rect);
        GuiShape(int x, int y, int width, int height);

        ~GuiShape();

        void draw();

        void setX(int value) { rectangle->setStartX(value);     }
        void setY(int value) { rectangle->setStartY(value);     }

        Rectangle * getRectangle() { return rectangle; }

        bool isMouseOverShape();

        void setColors(int darkBorder, int lightBorder, int inner);

        int getDarkBorderColor() { return darkBorderColor; }
        int getLightBorderColor() { return lightBorderColor; }
        int getInnerColor() { return innerColor; }

    protected:
        Rectangle * rectangle;

    private:
        bool rectangleOwner;
        int darkBorderColor;
        int lightBorderColor;
        int innerColor;
};

And finally GuiButton:

#ifndef CGUIBUTTON_H_
#define CGUIBUTTON_H_

#include <sstream>
#include <string>

#include "allegro.h"

#include "../../gameobjects/Rectangle.h"
#include "GuiShape.h"

class GuiButton : public GuiShape {

    public:
        GuiButton(Rectangle * rect, std::string theLabel);
        GuiButton(int x, int y, int width, int height, std::string theLabel);
        ~GuiButton();

        void draw();

        std::string * getLabel() {
            return label;
        }

        BITMAP * getBitmap() { return bitmap; }
        void setBitmap(BITMAP * value) { bitmap = value; }
        void setHasBorders(bool value) { hasBorders = value; }
        void setPressed(bool value) { pressed = value; }

        bool shouldDrawPressedWhenMouseHovers() { return drawPressedWhenMouseHovers; }
        bool shouldDrawBorders() { return hasBorders; }
        void setDrawPressedWhenMouseHovers(bool value) { drawPressedWhenMouseHovers = value; }
        bool isPressed() { return pressed; }

    private:
        std::string * label;
        bool drawPressedWhenMouseHovers;
        bool hasBorders;
        bool pressed;
        BITMAP * bitmap;

        void drawBackground();
        void drawLighterBorder();
        void drawDarkerBorder();
        void drawButtonUnpressed();
        void drawButtonPressed();
};

#endif /* CGUIBUTTON_H_ */

Which leads me to the following questions:

  • What is the best way to use virtual deconstructors where objects are derived from A->B->C ?
  • Should C only be the concrete virtual? And if so, how do you release resources defined and handled only in B? (A=GuiElement, B=GuiShape, C=GuiButton)
  • Why would I get 'undefined references' with the straight-forward implementation of A->B ? (GuiElement->GuiWindow)

Thanks in advance for your help!


What is the best way to use virtual deconstructors where objects are derived from A->B->C ?

mark the base's (or all) destructor as virtual.

Should C only be the concrete virtual? And if so, how do you release resources defined and handled only in B? (A=GuiElement, B=GuiShape, C=GuiButton)

Not sure what you mean by "concrete virtual" but a class with members that need destroying should destroy them in it's own destructor. No exceptions. when ~C is called, it destroys it's own stuff, and then ~B will be called automatically. The virtual just makes absolutely sure that ~C is called first.

Why would I get 'undefined references' with the straight-forward implementation of A->B ? (GuiElement->GuiWindow)

virtual ~GuiElement(); tells the compiler that the class has a destructor that will be defined later. You wanted either:

// There is no definition, cannot make a local "GuiElement" variable
// They can only make local "GuiButton" or other derived.
// You can still have pointers to a GuiElement.
// This is called "pure virtual"
virtual ~GuiElement() = 0; 

or:

// There is a definition, someone can make a local "GuiElement" variable
virtual ~GuiElement()  {};


I thought it was sufficient to have an implementation in the GuiWindow class? Is that correct?

No. A virtual function (that is not pure virtual, as your destructor of GuiElement) must be defined if it is declared in the class.

Destructors go even further: they must be implemented, always, even if it is pure virtual[1]. If you hadn't declared it, the compiler would create one (implicitly nonvirtual, but would be virtual if it would override a virtual destructor) for you. In C++11, you can just mark it "defaulted" (which means "compiler, implement that for me") and "deleted" which means "the program may never, implicitly or explicitly, destruct objects of this type".

  1. What is the best way to use virtual deconstructors where objects are derived from A->B- C ?

You usually want to make the topmost base's destructor virtual, that means all destructors in the hierarchy are virtual.

And if so, how do you release resources defined and handled only in B? (A=GuiElement, B=GuiShape, C=GuiButton)

In ~B(), naturally.

[1] :

12.4/7: A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly- declared) is virtual.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号