Принудительная установка производного класса без повторной реализации чистых виртуалов
Я определил класс интерфейса 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
для обоих путей будут созданы, и vtable
s будут заполнены с обоих путей.
Этот код компилируется:
#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;
}