Виртуальные деконструкторы в интерфейсе-> аннотация-> конкретный дизайн класса - PullRequest
0 голосов
/ 15 октября 2011

Я попытался ответить на это сам, посмотрев несколько вопросов в StackOverflow. И хотя я думаю, что понимаю это правильно, я не могу это исправить. Что оставляет меня с единственным очевидным наблюдением: я все еще не понимаю.

Я сделал резюме вопросов в нижней части этого поста, все, что между ними, - это информация, которую я собрал, и контекст для этого вопроса.

Итак, я понял, что когда у вас есть базовый класс и производный класс, ваш деконструктор должен быть помечен как виртуальный в базовом классе. Разрешить полиморфизм.

Но я не могу заставить свой код компилироваться, или когда он компилируется, он не связывается из-за «неопределенных ссылок». Я менялся взад и вперед, но мне, кажется, никогда не выходить из этого цикла.

По сути, у меня есть взаимодействие, определяемое так:

#ifndef GUIELEMENT_H_
#define GUIELEMENT_H_

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

#endif /* GUIELEMENT_H_ */

У меня есть несколько объектов, выходящих из этого. Простое отношение - это GuiWindow (напрямую происходит от 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_ */

Этот код не связывает, дает мне:

неопределенная ссылка на `GuiElement :: ~ GuiElement () '

Я думал, что было достаточно иметь реализацию в классе GuiWindow? Это правильно?

Следующее, что меня действительно беспокоит, это то, что у меня также есть абстрактный класс, производный от GuiElement, и конкретные реализации поверх этого. В основном дает: GuiElement-> GuiShape-> GuiButton

Вот заголовок 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;
};

И, наконец, 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_ */

Что приводит меня к следующим вопросам:

  • Каков наилучший способ использовать виртуальные деконструкторы, где объекты получены из A-> B-> C?
  • Должен ли C быть только конкретным виртуальным? И если так, как вы освобождаете ресурсы, определенные и обработанные только в B? (A = GuiElement, B = GuiShape, C = GuiButton)
  • Зачем мне получать «неопределенные ссылки» с прямой реализацией A-> B? (GuiElement-> GuiWindow)

Заранее спасибо за помощь!

Ответы [ 2 ]

5 голосов
/ 15 октября 2011

Каков наилучший способ использования виртуальных деконструкторов, где объекты получены из A-> B-> C?

пометить деструктор базы (или весь) как виртуальный.

Должен ли C быть только конкретным виртуальным?И если так, как вы освобождаете ресурсы, определенные и обработанные только в B?(A = GuiElement, B = GuiShape, C = GuiButton)

Не уверен, что вы подразумеваете под "конкретным виртуальным", но класс с членами, которые должны быть уничтожены, должен уничтожить их в своем собственном деструкторе.Без исключений.когда вызывается ~C, он уничтожает свой собственный материал, и тогда ~B будет вызываться автоматически .Виртуал просто гарантирует, что сначала будет вызван ~C.

Зачем мне получать «неопределенные ссылки» с прямой реализацией A-> B?(GuiElement-> GuiWindow)

virtual ~GuiElement(); сообщает компилятору, что в классе есть деструктор, который будет определен позже.Вы хотели:

// 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; 

или:

// There is a definition, someone can make a local "GuiElement" variable
virtual ~GuiElement()  {};
2 голосов
/ 15 октября 2011

Я думал, что было достаточно иметь реализацию в классе GuiWindow? Это правильно?

Нет. Виртуальная функция (которая не является чисто виртуальной, как ваш деструктор GuiElement) должна быть определена, если она объявлена ​​в классе.

Деструкторы идут еще дальше: они должны быть реализованы всегда, даже если они чисто виртуальные [1]. Если бы вы не объявили это, компилятор создал бы его (неявно не виртуальный, но был бы виртуальным, если он переопределил бы виртуальный деструктор) для вас. В C ++ 11 вы можете просто пометить его как «дефолтный» (что означает «компилятор, реализуйте это для меня») и «удаленный», что означает, что «программа никогда не может, явно или неявно, разрушать объекты этого типа».

  1. Каков наилучший способ использовать виртуальные деконструкторы, где объекты получены из A-> B- C?

Обычно вы хотите сделать виртуальный деструктор самой верхней базы, то есть все деструкторы в иерархии являются виртуальными.

И если так, как вы высвобождаете ресурсы, определенные и обработанные только в B? (A = GuiElement, B = GuiShape, C = GuiButton)

В ~B(), естественно.

[1]:

12.4 / 7: деструктор может быть объявлен виртуальным (10.3) или чисто виртуальным (10.4); если какие-либо объекты этого класса или любой Производный класс создается в программе, деструктор должен быть определен. Если класс имеет базовый класс с виртуальный деструктор, его деструктор (объявленный пользователем или неявно) виртуальный.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...