Часто бывает полезно иметь абстрактный базовый класс для изоляции интерфейса объекта.
Проблема в том, что конструкция копирования, IMHO, по умолчанию в C ++ в значительной степени нарушена, а конструкторы копирования генерируются по умолчанию.
Итак, что за ошибки, когда у вас есть абстрактный базовый класс и необработанные указатели в производных классах?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
А теперь вы полностью отключили конструкцию копирования для всей иерархии? Объявить копию конструкции как частную в IAbstract
?
Есть ли правила трех с абстрактными базовыми классами?
c++
abstract-class
кодировщик
источник
источник
Ответы:
Конструкция копирования в абстрактном классе в большинстве случаев должна быть закрытой, так же как и оператор присваивания.
Абстрактные классы по определению считаются полиморфными. Таким образом, вы не знаете, сколько памяти использует ваш экземпляр, и поэтому не можете безопасно скопировать или назначить его. На практике вы рискуете нарезать ломтиками: /programming/274626/what-is-the-slicing-problem-in-c
Полиморфный тип в C ++ нельзя манипулировать значением. Вы управляете ими по ссылке или по указателю (или любому интеллектуальному указателю).
Это причина, по которой Java сделала объект управляемым только по ссылке, и почему в C # и D есть разделение между классами и структурами (первый - полиморфный и ссылочный тип, второй - не полиморфный и тип значения).
источник
Конечно, вы можете просто сделать его защищенным и пустым, чтобы можно было выбирать производные классы. Однако, в более общем смысле, ваш код в любом случае запрещен, потому что его невозможно создать
IAbstract
- потому что он имеет чисто виртуальную функцию. Таким образом, это, как правило, не проблема - классы вашего интерфейса не могут быть созданы и, следовательно, никогда не могут быть скопированы, а ваши более производные классы могут запретить или продолжить копирование, как они пожелают.источник
operator=(const Derived2&)
вDerived1
.Делая ctor и присваивание частным (или объявляя их как = delete в C ++ 11), вы отключаете копирование.
Дело в том, ГДЕ вы должны это сделать. Чтобы остаться с вашим кодом, IAbstract не является проблемой. (обратите внимание, что делая то, что вы сделали, вы назначаете
*a1
IAbstract
подобъект a2, теряя все ссылки наDerived
. Присвоение значения не является полиморфным)Проблема идет с
Derived::theproblem
. Копирование производного в другое может фактически делиться*theproblem
данными, которые могут быть не предназначены для совместного использования (есть два экземпляра, которые могут вызыватьdelete theproblem
их деструктор).Если это так, то
Derived
это должно быть не подлежащим копированию и не подлежащим назначению. Конечно, если вы делаете закрытую копиюIAbstract
, так какDerived
она нужна по умолчанию ,Derived
она также не будет копироваться. Но если вы определяете свое собственноеDerived::Derived(const Derived&)
без вызоваIAbtract
copy, вы все равно можете копировать их.Проблема в Derived, и решение должно оставаться в Derived: если это должен быть динамический объект, доступ к которому есть только по указателям или ссылкам, то это должен быть сам Derived, который должен иметь
По сути, именно разработчик класса Derived (который должен знать, как работает Derived и как
theproblem
управляется), должен решить, что делать с назначением и копированием.источник