Принудительная установка производного класса без повторной реализации чистых виртуалов

Я определил класс интерфейса A который определяет некоторые основные функции. В моей реализации у меня есть базовый класс A0 который реализует этот интерфейс и из этого базового класса я вывел несколько других классов в иерархии.

#include <iostream>
#include <string>

class IContainer
{
public:
    IContainer() {};
    virtual ~IContainer() {};

    virtual void loadDefaults() = 0;
    virtual void storeDefaults() = 0;

    virtual bool open() = 0;
    virtual bool close() = 0;
};

class IContainerReader
{
public:
    IContainerReader() {};
    virtual ~IContainerReader() {};

    virtual bool read() = 0;
};

class IContainerWriter
{
public:
    IContainerWriter() {};
    virtual ~IContainerWriter() {};

    virtual bool write() = 0;
};

class ContainerBase : public IContainer
{
public:
    ContainerBase() {}
    virtual ~ContainerBase() {}

    void loadDefaults() {}
    void storeDefaults() {}
};

class CSVBase : public ContainerBase
{
public:
    CSVBase() {}
    virtual ~CSVBase() {}

    void setFilename() {}
    bool open() { return true; }
    bool close() { return true; }
};


class CSVReader : public CSVBase, public IContainerReader
{
public:
    CSVReader() {}
    virtual ~CSVReader() {}

    bool read() { return true; }
};

class CSVWriter : public CSVBase, public IContainerWriter
{
public:
    CSVWriter() {}
    virtual ~CSVWriter() {}

    bool write() { return true; }
};

int main(int argc, char *argv[])
{
    CSVReader r;
    CSVWriter w;
    IContainerReader *ir = &r;
    IContainerWriter *iw = &w;

    ir->open();
    iw->open();

    ir->read();
    iw->write();

    ir->close();
    iw->close();

    return 0;
}

Как вы можете видеть, я определил IContainerReader и IContainerWriter класс, который определяет специальные функции, относящиеся только к соответствующей реализации.

Но теперь у меня есть проблема, потому что я хочу быть уверенным, что Reader или Writer также всегда имеют контейнерную базу. Таким образом, логичным решением было бы получить IContainerReader/-Writer от IContainer, Но когда я делаю это, компилятор thge жалуется, потому что он ожидает, что теперь Reader/Writer Объект снова реализует базовые функции, которые уже определены через базовый класс. Но если я позволю IContainerReader не происходить от IContainer указатель на один из этих объектов не имеет IContainer функциональность также.

Если я пытаюсь скомпилировать это так, я получаю ошибки, потому что IContainerReader это не IContainer

||=== Build: Debug in CPPMingW (compiler: GNU GCC Compiler) ===|
D:\src\c\Tests\CPPMingW\main.cpp||In function 'int main(int, char**)':|
D:\src\c\Tests\CPPMingW\main.cpp|83|error: 'class IContainerReader' has no member named 'open'|
D:\src\c\Tests\CPPMingW\main.cpp|84|error: 'class IContainerWriter' has no member named 'open'|
D:\src\c\Tests\CPPMingW\main.cpp|89|error: 'class IContainerReader' has no member named 'close'|
D:\src\c\Tests\CPPMingW\main.cpp|90|error: 'class IContainerWriter' has no member named 'close'|
||=== Build failed: 4 error(s), 0 warning(s) (0 minute(s), 5 second(s)) ===|

Однако, если я выведу IContainerReader от IContainer как и должно быть, я получаю следующие ошибки:

||=== Build: Debug in CPPMingW (compiler: GNU GCC Compiler) ===|
D:\src\c\Tests\CPPMingW\main.cpp||In function 'int main(int, char**)':|
D:\src\c\Tests\CPPMingW\main.cpp|78|error: cannot declare variable 'r' to be of abstract type 'CSVReader'|
D:\src\c\Tests\CPPMingW\main.cpp|58|note:   because the following virtual functions are pure within 'CSVReader':|
D:\src\c\Tests\CPPMingW\main.cpp|11|note:     virtual void IContainer::loadDefaults()|
D:\src\c\Tests\CPPMingW\main.cpp|12|note:     virtual void IContainer::storeDefaults()|
D:\src\c\Tests\CPPMingW\main.cpp|14|note:     virtual bool IContainer::open()|
D:\src\c\Tests\CPPMingW\main.cpp|15|note:     virtual bool IContainer::close()|
D:\src\c\Tests\CPPMingW\main.cpp|79|error: cannot declare variable 'w' to be of abstract type 'CSVWriter'|
D:\src\c\Tests\CPPMingW\main.cpp|67|note:   because the following virtual functions are pure within 'CSVWriter':|
D:\src\c\Tests\CPPMingW\main.cpp|11|note:     virtual void IContainer::loadDefaults()|
D:\src\c\Tests\CPPMingW\main.cpp|12|note:     virtual void IContainer::storeDefaults()|
D:\src\c\Tests\CPPMingW\main.cpp|14|note:     virtual bool IContainer::open()|
D:\src\c\Tests\CPPMingW\main.cpp|15|note:     virtual bool IContainer::close()|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 6 second(s)) ===|

Таким образом, компилятор ожидает от меня повторной реализации всех функций из базовых классов в производном классе.

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

1 ответ

Решение

Надеюсь, я понял это правильно. У вас какая-то проблема с бриллиантами. Вы наследуете от IContainer через два пути.

Обычно в таких случаях у вас было бы два случая IContainer создан на один экземпляр CSVReaderи звонки IContainer методы были бы неоднозначными. В вашем случае путь через IContainerReader не определил упомянутые функции. Виртуальное наследование позволяет создать только один экземпляр.

Наследование от IContainer должен быть объявлен как virtual, Виртуальное наследование делает так, что каждый выход из класса "будет объединяться" (извините за не очень технические термины, но я так понимаю в простом английском). В вашем случае только одна копия IContainer для обоих путей будут созданы, и vtables будут заполнены с обоих путей.

Этот код компилируется:

#include <iostream>
#include <string>

class IContainer
{
public:
    IContainer() {};
    virtual ~IContainer() {};

    virtual void loadDefaults() = 0;
    virtual void storeDefaults() = 0;

    virtual bool open() = 0;
    virtual bool close() = 0;
};

class IContainerReader : virtual public IContainer
{
public:
    IContainerReader() {};
    virtual ~IContainerReader() {};

    virtual bool read() = 0;
};

class IContainerWriter : virtual public IContainer
{
public:
    IContainerWriter() {};
    virtual ~IContainerWriter() {};

    virtual bool write() = 0;
};

class ContainerBase : virtual public IContainer
{
public:
    ContainerBase() {}
    virtual ~ContainerBase() {}

    void loadDefaults() {}
    void storeDefaults() {}
};

class CSVBase : public ContainerBase
{
public:
    CSVBase() {}
    virtual ~CSVBase() {}

    void setFilename() {}
    bool open() { return true; }
    bool close() { return true; }
};


class CSVReader : public CSVBase, public IContainerReader
{
public:
    CSVReader() {}
    virtual ~CSVReader() {}

    bool read() { return true; }
};

class CSVWriter : public CSVBase, public IContainerWriter
{
public:
    CSVWriter() {}
    virtual ~CSVWriter() {}

    bool write() { return true; }
};

int main(int argc, char *argv[])
{
    CSVReader r;
    CSVWriter w;
    IContainerReader *ir = &r;
    IContainerWriter *iw = &w;

    ir->open();
    iw->open();

    ir->read();
    iw->write();

    ir->close();
    iw->close();

    return 0;
}
Другие вопросы по тегам