Наилучшая практика приведения наследуемого объекта в правильный дочерний класс - PullRequest
0 голосов
/ 19 ноября 2018

У меня два класса, у каждого из них по несколько детей:

class ContainerGeneral {...};
class ContainerTypeA : ContainerGeneral {
public:
    void doSomethingA();
};
class ContainerTypeB : ContainerGeneral {
    void doSomethingB();
};

class InterpreterGeneral {
protected:
    ContainerGeneral* container;
};

class InterpreterTypeA : InterpreterGeneral {
public:
    void saveContainer(ContainerTypeA* cont) {
        container = cont;
    }
};

class InterpreterTypeB : InterpreterGeneral {
public:
    void saveContainer(ContainerTypeB* cont) {
        container = cont;
    }
};

Классы Interpreter используются для контейнера соответствующего типа (A до A, B до B, General до General). Для этого я добавил к InterpreterGeneral указатель члена на объект ContainerGeneral. Я хочу, чтобы InterpreterGeneral обратился к этому объекту как ContainerGeneral, но я хочу, чтобы унаследованные классы могли обращаться к тому же контейнеру, что и контейнер соответствующего типа. Я могу сделать это, приведя указатель к унаследованному классу при обращении к нему (примеры только для A для экономии места):

(ContainerTypeA*)container->doSomethingA();

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

class InterpreterTypeA : InterpreterGeneral {
public:
    void saveContainer(ContainerTypeA* cont) {
        containerA = cont;
        container = cont;
    }
    void doSomething() {
        containerA->doSomethingA();
    }
private:
    ContainerTypeA* containerA;
};

Какова лучшая практика в этом случае, и есть ли способ сделать это настолько чистым, насколько это возможно, без разыгрывания каждый раз и без добавления новых членов, которые не содержат никакой «новой» информации?

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Проблема, с которой вы сталкиваетесь, имеет решение в виде виртуальных функций .Попробуйте это:

class ContainerGeneral {
public:
    virtual void doSomething() = 0;
};

class ContainerTypeA : public ContainerGeneral {
public:
    void doSomething() {
        std::cout << "Hello A!" << std::endl;
    };
};

class ContainerTypeB : public ContainerGeneral {
public:
    void doSomething() {
        std::cout << "Hello B!" << std::endl;
    };
};

С этим вам вообще не нужно беспокоиться о наследовании от InterpreterGeneral:

class InterpreterGeneral {
public:
    void doSomething()
    {
        container->doSomething();
    }
private:
    ContainerGeneral* container;
};

Примечание: ЭтоКонечно генерирует некоторые накладные расходы времени выполнения.Вы можете избежать этого, если вам не нужен полиморфизм во время выполнения.Взгляните на статический полиморфизм .Только если ты ниндзя.

0 голосов
/ 19 ноября 2018

Если вам нужна конкретная информация о типе экземпляра контейнера в пределах InterpreterTypeA и InterpreterTypeB, выразите это в своем коде, не сохраняя элемент данных GeneralContainer* в GeneralInterpreter (элемент данных protected весьма спорен вв любом случае), но вместо этого конкретные ContainerTypeA* и ContainerTypeB* элементы данных в подклассах InterpreterTypeA и InterpreterTypeB.Хранение указателя базового класса и последующее обход его ограничения путем приведения скрывает тот факт, что требуется конкретная информация о типе.

Кроме того, нет ничего плохого в том, чтобы либо предоставить пустые реализации по умолчанию doSomethingA() и doSomethingB() в базовом классе контейнера, либо превратить их в virtual чистые функции-члены и переместить пустую реализацию в ContainerTypeA и ContainerTypeB.Тогда просто безопасно вызывать их - когда это нежелательный конкретный тип, они ничего не будут делать.

Последнее, педантичное примечание: я не вижу никакой причины вызывать производные классы в иерархии"сын" классы.Общий термин "детский" класс.

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