Обновленный ответ.
std::aligned_union
(en.cppreference.com). Он предназначен для использования вместе с размещением нового и явного вызова деструктора.
Ниже ранее ответ, теперь убирается.
С точки зрения дизайна,
- избежать динамического распределения кажется резкое требование. Это требует особого оправдания.
- В случае, если по какой-либо причине никто не доверяет стандартному распределителю, все же можно реализовать собственный распределитель, чтобы иметь полный контроль над его поведением.
- Если есть класс или метод, который «владеет» всеми экземплярами всего: все Заводы, все Процессоры и все Задачи (как в вашем методе
main()
), тогда нет необходимости копировать что-либо. Просто передайте ссылки или указатели, так как этот «класс или метод, который владеет всем» будет заботиться о жизни объекта.
Мой ответ применим только к вопросу о «тетсру».
Я не пытаюсь охватить проблему тетсра-тов между «Задачей, которая имеет интерфейс в качестве базового класса и X, который имеет интерфейс, как член». Это не кажется универсальным для всех компиляторов C++, но я не знаю, из каких компиляторов C++ выйдет этот код.
Короткий ответ, который применим для всех компиляторов C++:
В настоящее время тривиальной Copyable списков «нет виртуальных функций» в качестве одного из необходимых условий, так что «в соответствии со спецификацией» Ответ в том, что ваша структура Task
не тривиальный копируемая.
Чем дольше, нестандартный ответ, будет ли конкретный компилятор синтезировать структура и машинный код, который будет эффективно копируемый (т.е. без вредных побочных эффектов), несмотря на C++ спецификации сказать нет. Очевидно, что этот ответ будет специфичным для компилятора и будет зависеть от множества обстоятельств (таких как флаги оптимизации и незначительные изменения кода).
Помните, что оптимизация компилятора и генерация кода могут изменяться с версии на версию. Нет никакой гарантии, что следующая версия компилятора будет вести себя точно так же.
Чтобы дать пример того, что было бы, вероятно, будет небезопасны для тетсра-ния между двумя экземплярами, рассмотреть следующие вопросы:
struct Task : public Interface
{
Task(std::string&& s)
: data(std::move(s))
{}
virtual void execute() { std::cout << data << std::endl; }
std::string data;
};
Причины этого является проблематичным является то, что при достаточно длинными строками, std::string
будет выделять динамическую память для хранения ее содержимого. Если есть два экземпляра Task
, а memcpy используется для копирования его байтов из одного экземпляра в другой экземпляр (который бы скопировал по внутренним полям класса std::string
), их указатели укажут на один и тот же адрес, и, следовательно, их деструкторы оба попытаются удалить одну и ту же память, что приведет к неопределенному поведению. Кроме того, если экземпляр, который был перезаписан, имел более раннее строковое значение, память не будет освобождена.
Поскольку вы сказали, что «динамическое распределение запрещено», я предполагаю, что вы не будете использовать std::string
или что-нибудь подобное, вместо этого предпочитаете писать только C-код. Поэтому эта озабоченность может не иметь отношения к вам.
Говоря о «низком уровне C-подобный код», вот моя идея:
struct TaskBuffer
{
typedef void (*ExecuteFunc) (TaskBuffer*);
ExecuteFunc executeFunc;
char padding[1024];
};
void ProcessMethod(TaskBuffer* tb)
{
(tb->executeFunc)(tb);
}
См.: ['Std :: aligned_union' (ru.cppreference.com)] (http://en.cppreference.com/w/cpp/types/aligned_union). Он предназначен для использования вместе с размещением нового и явного вызова деструктора. – rwong