У меня есть случай, когда у меня есть контейнер указателей на базовый класс, и некоторые из этих указателей фактически указывают на объекты производного класса. Мне нужно создавать копии каждого из объектов в контейнере, не разрезая производные «части» объектов в процессе копирования.
Обычный способ сделать это — реализовать метод, подобный методу «виртуального родительского клона ()». Это требует, чтобы каждый класс в иерархии реализовал свой собственный метод clone (), используя свой собственный тип. Это требует, чтобы будущие еще неписанные дочерние классы реализовывали поведение, ожидаемое родительским классом, но без возможности принудительного выполнения этого. Вместо этого я написал класс «адаптера», от которого производные классы могут наследовать, а не напрямую наследовать от базового класса. Делая это, я могу обеспечить согласованность в еще не написанных дочерних классах.
Класс адаптера (промежуточный уровень в иерархии, между родительским и дочерним) должен иметь знание дочернего класса, чтобы вызвать «new ()» для правильного типа дочернего класса. Это «чувствует», как будто это несколько нарушает традиционную объектно-ориентированную модель, заставляя родителя знать классы, которые наследуют его. Однако это может быть оправдано с точки зрения общего программирования. Это, кажется, компилировать и работать.
Я ищу какие-либо критические замечания, на которые следует обратить внимание и т. Д. Является ли этот код на 100% переносимым и совместимым со стандартами? Есть ли лучший шаблон дизайна, который я мог бы использовать вместо этого? Этот адаптер полезен для других в общих целях? Спасибо,
Шон
#include <cstdlib>
#include <iostream>
#include <stdio.h>
//-----------------------------------------
//Don't need to forward-declare bar; just needs to be defined before first instantiation
template<typename A, typename B>
class foo
{
public:
foo()
{
printf("foo::foo() : constructed a foo.\n");
}
virtual foo<A, B>* clone()
{
printf("foo::clone() : making a clone...\n");
return new foo<A, B>;
}
virtual void DoSomething()
{
printf("foo::DoSomething() : something...\n");
}
A myA;
B myB;
};
template<typename A, typename B, typename ChildClass>
class fooAdapter : public foo<A, B>
{
public:
fooAdapter()
{
printf("fooAdapter::fooAdapter() : constructed a fooAdapter.\n");
}
foo<A, B>* clone()
//or directly take in foo<>'s complete type rather than parametric per-parameter
{
return new ChildClass( *((ChildClass*) this) );
}
};
//-----------------------------------------
class bar : public fooAdapter<int, float, bar>
{
public:
bar()
{
printf("bar::bar() : constructed a bar.\n");
}
bar(const bar& myBar)
{
printf("bar::bar(bar) : copy-constructing a bar.\n");
}
virtual void DoSomething()
{
printf("bar::DoSomething() : something.\n");
};
//don't need to include clone() in bar
};
//-----------------------------------------
int main(int argc, char *argv[])
{
printf("About to construct myBar.\n");
bar myBar;
myBar.myA = 2;
myBar.myB = 1.5;
printf("\nAbout to clone myBar to fooTest.\n");
foo<int, float>* fooTest = myBar.clone();
fooTest->DoSomething();
printf("\nAbout to construct fooTest2 from fooTest.\n");
foo<int, float>* fooTest2 = new bar( *((bar*) fooTest) );
return EXIT_SUCCESS;
}
Прекратите использовать тупые указатели.
Напишите дополненное значение с помощью операции приведения к основанию или значения ptr и функции make value, которая напрямую создает значение ptr значения.
Оба знают динамический тип объекта, и, поскольку они отказываются брать указатели, они вряд ли будут созданы полиморфно.
template<class Base>
struct poly_any:private std::any{
Base* operator->(){ return to_base(*this); }
template<class X, std::enable_if<std::is_base_of<Base, std::decay_t<X>>{}, bool> =true>
poly_any(X&& x):
std::any(std::forward<X>(x)),
to_base([](std::any& self)->Base*{
return std::any_cast<std::decay_t<X>*>(&self);
})
{}
poly_any(poly_any const&)=default;
poly_any(poly_any &&)=default;
poly_any&operator=(poly_any const&)=default;
poly_any&operator=(poly_any &&)=default;
private:
using to_base_t = Base*(*)(std::any&);
to_base_t to_base=nullptr;
};
этот poly_any<Base>
представляет собой полиморфный тип значения любого типа, полученный из Base
, Нужна какая-то работа, но это главное.
Мы не переписываем никого, чтобы написать clone
, мы используем any
набрать стереть копирование и пользовательский тип стереть приведение к базе.
Вы все еще можете нарезать это, но это происходит в первый раз, когда вы кладете что-то в poly_any
,
std::any
может быть написано, если вам не хватает, или вы можете использовать boost::any
, Там могут быть опечатки.
Других решений пока нет …