LINUX.ORG.RU

C++, ссылки и полиморфизм

 


0

3

Здравствуйте. Есть такой код:

class Base
{
public:
    virtual void print() const
    {
        cout << "base print" << endl;
    }
};

class Derived: public Base
{
public:
    void print() const override
    {
        cout << "derived print" << endl;
    }
};

class Some
{
    Base& obj;

public:
    Some(Base&& a_obj)
        : obj(a_obj)
    {
    }

    void objPrint() const
    {
        obj.print();
    }
};

int main(int, char**)
{
    Some&& s = Some(Derived());
    s.objPrint();
}
Здесь ссылка Some::obj инициализируется объектом типа Derived и в методе Some::objPrint() интерпретируется с типом Derived&, в результате выполнения видим «derived print», но если добавить в класс Base виртуальный деструктор, Some::obj интерпретируется с типом Base& и в результате выполнения - «base print». Почему так происходит, что почитать?

но если добавить в класс Base виртуальный деструктор, Some::obj интерпретируется с типом Base& и в результате выполнения - «base print».

это 4.2.

asaw ★★★★★ ()

Говнокод

Some&& s = Some(Derived());

в данной строке ты сохраняешь ссылку на временный объект. Этот говнокод не обязан работать.

anonymous ()
Ответ на: Говнокод от anonymous

Ага, я в курсе, но:

    Some s = Some(Derived());
    s.objPrint();
результат тот же. И как вообще это должно повлиять?

melvin ()
Ответ на: комментарий от melvin

Ты совершенно не то исправил. Проблема в Derived().

anonymous ()

Если сделать Some::obj указателем - всё работает как надо.

nanoolinux ★★★★ ()

с++? ты не умеешь его готовить.

#include <iostream>
using namespace std;

class Base
{
  public:
    virtual void print() const = 0;
    virtual ~Base() {}
};

class Derived: public Base
{
  public:
    void print() const {
      cout << "derived print" << endl;
    }
};

class Some
{

  Base& obj;

  public:
    Some(Base& a_obj): obj(a_obj){}

    void objPrint() const {
      obj.print();
    }
};

int main(int, char**)
{
  Derived d;
  Some s = Some(d);
  s.objPrint();
}

nanoolinux ★★★★ ()

obj(a_obj)

Не смущает, что ты Some::obj инициализируешь ссылкой на временный объект?

Pavval ★★★★★ ()
Ответ на: комментарий от Pavval

С этим более-менее понятно, но почему, когда этот временный объект разрушается objPrint() все же отрабатывает, ведь, по идее, Some::obj ссылается на уже разрушенный объект?

melvin ()
Ответ на: комментарий от nanoolinux

Если не разберусь со ссылками, то так и будет, просто для себя интересно, видимо я <Type>&& неправильно понимаю.

melvin ()
Ответ на: комментарий от melvin

Я надеюсь ты понимаешь, что неправильно в твоей первой версии?

nanoolinux ★★★★ ()
Ответ на: комментарий от melvin

С этим более-менее понятно, но почему, когда этот временный объект разрушается objPrint() все же отрабатывает

Потому что компилятор так решил. Взять -O2 и он может SIGSEGV сделать. А вообще это UB.

Если хочешь чтобы было как есть, то тащи Derived в кучу, то есть Some s(collect(new Derived())); через collect какого-нибудь garbage pool-а. Либо делай как предложили выше — укладывай объекты на стеке.

quasimoto ★★★★ ()
Ответ на: комментарий от melvin

а что ты понимаешь под словом разрушается? ячейки памяти уничтожаются аппаратно или что?

ckotinko ☆☆☆ ()
Ответ на: комментарий от ckotinko

Ага, т.е. просто волей случая у меня там подходящие данные. С этим все ясно, спасибо.

melvin ()
Ответ на: комментарий от melvin

но почему, когда этот временный объект разрушается objPrint() все же отрабатывает, ведь, по идее, Some::obj ссылается на уже разрушенный объект?

Нет явного доступа к this, но есть неявный через vptr. Сам временный объект все еще в стеке main, хоть и уже разрушен. SIGSEGV нет, потому что адреса валидные.

Когда ты ставишь виртуальные деструкторы, перед вызовом ~Base() vptr откатывается с Derived до Base и потому вызывается Base::print(). Иначе вызывается только ~Derived() и потом через vptr вызывается Derived::print().

Pavval ★★★★★ ()
Ответ на: комментарий от melvin

Если не разберусь со ссылками, то так и будет, просто для себя интересно, видимо я <Type>&& неправильно понимаю.

Пойми - твой объект где-то должен храниться, чтобы ты его мог вызвать. Т.к. ты не хочешь Derived d;, то объект должен будет храниться в Some (куда переместится из временного Derived()). Тогда тебе нужно заменить ссылку на сам объект:

class Some
{
    Base obj;

Или, если ты не хочешь копирования объекта, то юзай unique_ptr<Base> в Some и требуй в конструкторе или его же, или указатель на Base.

Pavval ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.