Как использовать reinterpret_cast для приведения к производному указателю класса в C ++ - PullRequest
5 голосов
/ 14 сентября 2011

Вот мой тестовый пример:

struct base {
    virtual ~base(){}
    int x;
};

struct derived: public virtual base {
    base * clone() {
        return new derived;
    }
    derived(): s("a") {}
    std::string s;
};

int main () {
    derived d;
    base * b = d.clone();
    derived * t = reinterpret_cast<derived*>(b);
    std::cout << t->s << std::endl;
    return 0;
}

Вылетает в строке, где я печатаю s. Поскольку «b» является указателем на производный класс, reinterpret_cast должен просто работать. Интересно, почему это вылетает? В то же время, если я заменю reinterpret_cast на dynamic_cast, то это сработает.

Ответы [ 3 ]

11 голосов
/ 14 сентября 2011

Даже если b здесь динамически типа derived, вы должны использовать dynamic_cast.Это то, для чего dynamic_cast предназначен для динамического преобразования указателя базового класса в производный класс во время выполнения.

reinterpret_cast принимает необработанный указатель и считает его производным типом.Однако из-за наследования virtual необходимо немного изменить указатель, чтобы он указывал на правильную таблицу диспетчеризации методов, и именно это будет делать dynamic_cast.

1 голос
/ 14 сентября 2011

Не reinterpret_cast, это вызовет проблемы с множественным или виртуальным наследованием, как в вашем случае. Разве просто static_cast делать работу здесь?

Чтобы узнать почему, ищите реализации виртуального наследования. Распространенным является хранение указателя на базовый класс внутри объекта, поэтому виртуальная база не имеет тот же адрес, что и ее производные классы. Существует аналогичный случай, когда используется множественное наследование.

Короче говоря, reinterpret_cast не может сделать намного больше, чем приведение указателей к int и обратно (если в int достаточно размера для размещения указателя).

0 голосов
/ 14 сентября 2011

Как предлагают другие ответы здесь, вы не можете использовать reinterpret_cast таким образом, потому что значение указателя до base фактически отличается от значения указателя доderived.Действительный указатель выводится во время выполнения, поэтому вы должны использовать dynamic_cast.static_cast не может работать, поскольку вы не знаете во время разработки, через какой промежуточный тип был получен наиболее производный класс (тот, который вы хотите привести) к типу, на который у вас есть указатель.

Реальный вопрос здесь должен быть следующим: во время разработки я знаю, как вычислить указатель derived из указателя base.Как избежать штрафа за время выполнения (из dynamic_cast)?

Честно говоря, я не вижу здесь действительно хорошего варианта, но вариант возможный - сохранить указатель насамый производный тип в константном указателе внутри корневого класса, например:

struct base {
  void* const self;
  virtual ~base() {}
  protected:
    base(void* self) : self(self) {}
};
struct derived : public virtual base {
  derived() : base(this) {}
}

Это уродливо и опасно, потому что жертвует безопасностью типов для производительности (если вам действительно повезет, вы получите незначительная производительность во время выполнения из него).Но вы сможете reinterpret_cast ваш base указатель (self член типа void*) в derived указатель.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...