class FileSystem1 : public IFile, public IDirectory
Давайте сидеть сложа руки и на минуту задуматься об этом. Это утверждает, что FileSystem1
равно (или, более формально, при любых возможных обстоятельствах, может использоваться вместо) либо IFile
, либо IDirectory
.
По крайней меретак как большинство людей используют эти термины, это совсем не так. Поскольку большинство людей используют термины, файловая система содержит некоторые вещи, каждая из которых может быть либо файлом, либо каталогом:
class FS_node {
virtual std::string name() const { return name_; }
// ...
virtual ~FS_node = default;
};
class File : public FS_node {
// ...
};
class Directory : public FS_node {
// ...
};
class FileSystem {
std::vector<FS_node *> nodes;
public:
// ...
};
Теперь, исходя из звука вещей, выприходится иметь дело с двумя совершенно отдельными файловыми системами. Есть несколько способов сделать это. Одной из возможностей будет иметь базовый класс FileSystem, который определяет интерфейс к файловой системе, а затем иметь два производных класса, которые реализуют этот интерфейс в терминах двух отдельных API на уровне ОС.
Другая возможность заключается вреализовать аналогичную функциональность, но вместо использования наследования вы должны указать класс интерфейса API в качестве параметра шаблона при создании экземпляра объекта FileSystem
:
template <class Api>
class FileSystem {
Api api;
public:
FileSystem(Api const &api) : api(api) {}
// FS functions for finding files and such go here,
// each implemented via the `Api` passed as a parameter
};
Что касается различия между использованием шаблонов и наследованием:это почти так же, как обычно: шаблоны статичны, поэтому, если (например) вам нужен код, который вы можете указать во время компиляции, компилировать ли для Windows или Linux, шаблоны должны работать хорошо. С другой стороны, если вы имеете дело с чем-то вроде одной коллекции файловых систем, и эта коллекция может содержать смесь объектов, каждый из которых представляет отдельную файловую систему, и вы хотите иметь возможность прозрачно работать со всеми нимиво время выполнения вам, вероятно, придется использовать иерархию наследования.
Но, по крайней мере, мне кажется вполне вероятным, что ваш оригинальный дизайн с FileSystem
получен из File
и Directory
занятия почти наверняка довольно серьезная ошибка. Возможно, нам нужно было бы узнать немного больше о том, что вы делаете, чтобы быть уверенным в том, какой подход действительно оптимален, но это, вероятно, не так.
На мгновение игнорируя все это о дизайне, иРассматривая вопрос о том, как преобразовать указатель в производное в указатель на основание, мы действительно имеем только два случая, которые имеют большое значение. Если вы использовали публичное наследование (как показано в вопросе), преобразование вообще не требует приведения:
FileSystem1 foo1;
IFile *fileBase = &foo1; // No problem.
IDirectory *dirBase = &foo1; // Likewise
Если вы использовали частное наследование, то вы только что обнаружили исключительно редкую ситуацию:тот, где вам действительно нужно использовать приведение в стиле C для правильного преобразования:
class Base1 {};
class Base2 {};
// Note: private derivation:
class Derived : Base1, Base2 {};
Derived d;
Base1 *b1 = (Base1 *)&d;
Base2 *b2 = (Base2 *)&d;
В этом конкретном случае (преобразование производного класса в недоступный базовый класс) ни одно из "новых" приведений C ++Вы можете сделать эту работу - вы должны нас сыграть в стиле C.