Есть такое интересное понятие, о котором вы возможно ни разу не слышали. Пример из поста выше с методами clone и create можно было написать иначе:
class Shape {
public:
virtual ~Shape() { } // A virtual destructor
// ...
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
public:
Circle* clone() const override;
Circle* create() const override;
// ...
};
Circle* Circle::clone() const { return new Circle(this); }
Circle* Circle::create() const { return new Circle(); }Вы скажете: "Сигнатуры не совпадают! Код не скомпилируется!".
А я скажу: "Shape и Circle - ковариантные типы". С++ разрешает наследнику переопределять методы с возвращаемым типом, который является наследником типа метода из базового класса. Говорят, что это даже называется идиомой С++.
Какие юзкейсы у этой идиомы? По факту всего один. Представьте, что все методы возвращают один тип Shape. Вы создали объект Circle в куче и присвоили указатель на него к указателю на Circle. Тогда при клонировании объекта Circle вам вернется указатель на объект базового класса. И по хорошему его надо динамик кастить к Circle, чтобы работать с конкретным типом наследника. А это оверхэд:
Circle *circle1 = new Circle();
Shape *shape = d1->clone();
Circle *circle2 = dynamic_cast<Circle *>(shape);
if(circle2) {
// Use circle2 here.
}
Выглядит не очень. Посмотрим, как изменится код, если методы Circle будут возвращать указатель на Circle:
Circle *circle1 = new Circle();
Circle *circle2 = d1->clone();
Выглядит намного лучше. Но вот вопрос: почему вы нигде не увидите в коде применения ковариантных типов?
Потому что этот подход не работает с умными указателями, которые де факто являются стандартом при возвращении объектов из фабрик. std::unique_ptr<Circle> не является наследником std::unique_ptr<Shape>, поэтому они и не ковариантные типы и сигнатуры методов будут несовместимы.
Возвращение сырых указателей - супер bad practice, один только этот факт заставляет отказаться от такого подхода.
Тем более полиморфные объекты и придумали для того, чтобы использовать их полиморфно. То есть через ссылку или указатель на базовый класс. Зачем оперировать полиморфным объектом с указателем на конкретный тип - не очень понятно.
Раньше, до изобретения умных указателей, идиома была легитимна. Теперь же она отправилась на свалку истории.
Только что вы прочитали очередную статью про совсем ненужную хрень. Ставьте 🗿, если ваше лицо сейчас на него похоже)
Stay poker-faced. Stay cool.
#fun #cppcore