Наткнулся в "More Exceptional C++" на информацию, что виртуальные методы могут вместо указателей на объекты базового класса возвращать объекты перегруженного класса. Решил попробовать смоделировать "самоопределяющийся" объект - с виртуальным методом, вместо указателя базового класса возвращающим указатель на объект производного, чтобы можно было вызывать функции, отсутствующие в базовом классе. Вот пример с "виртуальным клонированием":
#include
class Base
{
public:
Base(){}
virtual ~Base()=0;
virtual Base* clone()
{
qDebug()<<"Base clone";
return 0;
}
};
Base::~Base(){}
class Derived: public Base
{
public:
Derived(){}
virtual Derived* clone()
{
qDebug()<<"Derived clone";
return new Derived(*this);
}
QString derivedOnly(){return "derivedOnly";}
virtual ~Derived(){}
};
int main(int argc, char *argv[])
{
Derived d1;
Base * b1=&d1;
Base * b2=b1->clone(); b2=b2;
Derived * d2=d1.clone(); delete d2;
// Derived * d3=b1->clone(); delete d3;
}
Так всё работает, и вывод qDebug показывает, что запускается клонирование производного класса. Однако, раскомментировав последнюю строку, при компиляции получаю сообщение об ошибке "invalid conversion from 'Base' to 'Derived' [-fpermissive]".
Выводы:
Очевидно, что компилятор (g++, Windows/MinGW) проигнорировал описание Derived * Derived::clone(), сгенерировав вместо него код для Base * Derived::clone().
Эту подмену транслятор сделал, никак меня не уведомив. Я рассчитываю получить указатель типа Derived, а получаю Base - и ничего об этом не знаю!
Я понимаю, для чего это сделано. Я мог бы написать что-нибудь типа b1->clone()->derivedOnly(), и транслятор не знал бы, допустима ли такая конструкция, так как статически неизвестно, будет ли при исполнении b1 указывать на Base или на Derived.
Вопросы:
Почему так происходит - является ли это частью стандарта (где можно посмотреть?) или самоуправством g++ (4.6.2)?
Можно ли сделать так, чтобы выдавался warning?
Ведут ли себя так же другие трансляторы?
Есть ли возможность решить изначальную задачу, то есть "развернуть" объект из указателя на базовый класс, не указывая, в какой именно класс мы его разворачиваем (т. е. без dynamic_cast'ов)?
Ответ
О выводах
Выводы неверные. Пункт 3 верный. Он же является ответом на Ваш вопрос.
А вот пункт 1 и, следовательно, 2 - неверные.
Derived* Derived::clone();
Base* Base::clone();
Какой из методов Вы бы выбрали, будь у Вас указатель на тип Base*?
Ответы
1) Вы же сами отвечаете себе на этот вопрос в выводе 3:
статически неизвестно, будет ли при исполнении b1 указывать на Base или на Derived
Это приводит к тому, что компилятор использует статический тип объекта, то есть Base*. У объекта такого типа метод Base* clone() возвращает Base*, чтобы привести его к Derived* нужно явное преобразование.
Ссылка на стандарт: раздел 10.3.8
[...] its result is converted to the type returned by the (statically chosen) overridden function (5.2.2)
2) Тут я поразмышляю о другом warning'е. Который не будет показан, если написать, например:
Derived* d2 = dynamic_cast
Комментариев нет:
Отправить комментарий