В настоящее время я пытаюсь создать пару классов, которые зависят друг от друга. По сути, объекты класса B
создают объекты класса A
Тем не менее, я также использую иерархию наследования, поэтому все производные класса B
также должны иметь возможность создавать производные класса A
(каждая производная от B
соответствует производной от A
, поэтому DerB1
создает объекты DerA1
, а DerB2
создает объекты DerA2
),
У меня проблемы с моей реализацией, и это может быть глупо, но я хотел бы узнать, знает ли кто, что делать. Мой код ниже (я НИЧЕГО читаю код других людей, поэтому я попытался сделать его максимально простым для чтения... только несколько важных бит, которые я прокомментировал, чтобы объяснить)
class BaseB {} // Declare BaseB early to use in BaseA constructor
class BaseA
{
public:
BaseA(BaseB* b) {}; // Declare the BaseA constructor (callable by all B classes, which pass a pointer to themselves to the constructor so the A objects can keep track of their parent)
}
class DerA:public BaseA
{
DerA(BaseB* b):BaseA(b) {}; // Inherit the BaseA constructor, and use initialization list
}
class BaseB
{
public:
virtual BaseA createA() = 0; // Virtual function, representing method to create A objects
}
class DerB:public BaseB
{
BaseA createA() {
DerA* a = new DerA(this); // Definition of createA to make a new A object, specifically one of type DerA (Error1: No instance of constructor "DerA::DerA" matches the argument list)
return a; // Error2: Cannot return DerA for BaseA function
}
}
Итак, у меня есть две основные проблемы: одна из них практична (Error1, поскольку я просто просто неправильно вызываю функцию, даже если я пытаюсь this
), один из них философский (ошибка 2, поскольку я не знаю, как реализовать функции, которые я хочу. Если кто-нибудь может указать, почему возникает Error1, это было бы замечательно! Ошибка2, однако, требует некоторого объяснения.
Я хотел бы, чтобы мой пользователь (программист) взаимодействовал со всеми объектами A
одинаковым образом. Они будут иметь одинаковые точные публичные функции, но каждая из них будет ОЧЕНЬ различной реализации этих функций. Некоторые из них будут использовать разные типы данных (и поэтому потребуются контракты функций), но многие из них будут иметь одинаковые типы данных только с различными алгоритмами, которые они используют на них. Я хотел бы, чтобы часть кода работала точно так же, если используется один производный класса A
или другой. Однако в моей текущей реализации кажется, что мне нужно вернуть объект DerA
вместо объекта BaseA
(на сайте Error2). Это означает, что мне нужно будет написать сегмент основного кода SPECIFICALLY для объекта DerA
вместо любого произвольного объекта A
Мне хотелось бы что-то вроде:
BaseB b = new DerB(); // Declare which derivative of BaseB I want to use
BaseA a = b->createA(b); // Call the createA function in that derivative, which will automatically make a corresponding A object
Таким образом, я могу просто выбрать, какой тип объекта B
я бы хотел использовать в первой строке (по моему выбору конструктора B
или тега или шаблона или что-то еще), а остальная часть кода будет выглядеть одинаково для любого типа объекта B
(поскольку каждый из них имеет одинаковые функции публичных членов, хотя каждый объект будет выполнять эти функции по-разному).
Будет ли я лучше использовать шаблоны или какой-либо другой метод вместо наследования? (Прошу прощения за то, что вы намеренно расплывчаты, но я надеюсь, что мой пример класса A/B должен в основном объяснить, что мне нужно).
Спасибо за любую помощь. Приношу свои извинения за то, что вы задали два вопроса в одном посте и за долгое время, но я пытаюсь изучить лучший способ приблизиться к довольно большому перепроектированию некоторого программного обеспечения.
У вас есть несколько синтаксических проблем для устранения ошибок:
;
после каждого определения класса.class BaseB/*{} NO!!*/;
public:
сделать конструктор DerA
доступным для DerB
BaseA createA()
должен возвращать значение, а не указатель (согласно сигнатуре): return *a;
Существует еще одна потенциальная проблема скрытого среза, поскольку createA()
возвращает значение, а не указатель. Это означает, что ваш возвращенный объект (здесь *a
) будет скопирован, но как реальный объект BaseA. Таким образом, будет скопирована только часть BaseA объекта, а не производная часть. Это может привести к неожиданным неожиданностям.
Чтобы избежать нарезки, подумайте о возврате указателя, соответственно изменив подпись createA()
. Указанный объект затем сохранит правильный тип, не потеряв ничего.
Если позже вам нужно будет скопировать объект, вы можете использовать статический приведение, если вы абсолютно уверены в действительном типе объекта, на который указывают:
BaseA *pba = pdb->createA(); // get pointer returned
DerA da = *static_cast<DerA*>(pba); // static cast with pointer
Если вам нужно будет скопировать выделенные объекты BaseA, не обязательно обязательно указывая их реальный тип, вы можете реализовать функцию виртуального клонирования в DerA (например, образец шаблона прототипа)
DerA
, но я бы хотел иметь возможность ссылаться на него, не разрезая членов подкласса. Можно ли использовать «умные указатели» или динамическое приведение?
BaseA *createA()
и возвращаете указатель, вы избегаете нарезки (т. BaseA *createA()
Объект, на который указывает указатель, имеет правильный тип). Если вам нужен объект DerA
и вы уверены, что указатель указывает на такой объект, вы можете написать что-то вроде DerA da = *static_cast<DerA*>(pba);
class BaseB {}
должен бытьclass BaseB;
virtual BaseA createA()
Вы должны возвращать не по значению, а по (умному) указателю. См. En.wikipedia.org/wiki/Object_slicing