Рассмотрим такой фрагмент кода:
struct A {
A() {};
~A() { ::std::cout << "A::~A()\n"; }
};
struct B : public A {
B() {};
~B() { ::std::cout << "B::~B()\n"; }
};
int main(void)
{
{
const A& a = B();
}
return 0;
}
Вопрос: что будет выведено в результате выполнения кода?
Правильный ответ:
A::~A()
Почему? Стандарт языка C++ указывает, что привязка временного объекта к константной ссылке увеличивает время жизни объекта до времени жизни константной ссылки.
Из кода нельзя убрать const
, потому что в присваивании вызов конструктора возвращает, по сути дела, временный объект (rvalue в терминологии стандарта), а привязывать к неконстантным ссылкам можно только lvalue.
Есть маленький нюанс: вышесказанное не применимо к членам класса:
#include <string>
struct A {
A(void) { ::std::cout << "A::A()\n"; };
~A() { ::std::cout << "A::~A()\n"; }
};
struct B {
const A& a;
B(const A& ra) : a(ra) {};
~B() { ::std::cout << a.s << "\nB::~B()\n"; }
};
int main(void)
{
B b(A("test"));
return 0;
}
Деструктор A::~A()
будет вызван после выполнения конструктора B::B()
, это надо иметь в виду.
В случае, когда ссылка покидает пределы видимости, компилятор вызовет тот же деструктор, что и для временного объекта (на который эта ссылка ссылается). В результате мы получаем вызов правильного деструктора без виртуальных функций и расходов, с ними связанных.
Тем не менее, настоящим полиморфизмом это назвать нельзя. Рассмотрим пример:
struct A {
A() {};
~A() { ::std::cout << "A::~A()\n"; }
void test(void) const { ::std::cout << "A::test()\n"; }
};
struct B : public A {
B() {};
~B() { ::std::cout << "B::~B()\n"; }
void test(void) const { ::std::cout << "B::test()\n"; }
};
int main(void)
{
const A& a = B();
a.test();
return 0;
}
Метод test
просто обязан быть константным, ибо при использовании константной ссылки мы имеем дело с константным объектом.
Такой код выдаст следующий результат:
B::~B()
A::~A()
Как видим, полноценного полиморфизма не получается.
Всё же интересно, какое применение данному подходу можно найти?