Можно ли вызвать виртуальный метод производного объекта при понижении из базового класса? - PullRequest
2 голосов
/ 14 апреля 2011

Учитывая следующую структуру класса:

class Base
{
    virtual void outputMessage() { cout << "Base message!"; }
};

class Derived : public Base
{
    virtual void outputMessage() { cout << "Derived message!"; }
}

.. и этот фрагмент кода:

Base baseObj;
Derived* convertedObj = (Derived*) &baseObj;
convertedObj->outputMessage();

.. на выходе будет «Базовое сообщение!».

Есть ли способ приведения или манипулирования объектом, чтобы сделать версию Derived метода outputMessage полиморфно вызываемой?

Edit : я попытаюсь показать причину, по которой япосле этого:

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

Я также пытался создавать объекты производного класса напрямую, но этовызывает проблемы в других частях системы из-за манипулирования объектами, передаваемыми через конструктор.

Ответы [ 5 ]

5 голосов
/ 14 апреля 2011

Нет, виртуальные функции работают с реальными типами объекта, на который указывает объект, который в вашем случае является простым Base.
На самом деле, при понижении вы входите в землю неопределенного поведенияВот.Это может взорваться как бомба с множественным наследованием, когда виртуальная таблица в производном классе не совпадает с виртуальной таблицей в базовом классе.

1 голос
/ 14 апреля 2011

Нет Стандартно-совместимого решения

То, что вы пытаетесь сделать , невозможно при использовании поведения, гарантированного стандартом C ++ .

Если вы действительно ДОЛЖНЫ это сделатьэто как краткосрочная мера, чтобы помочь вашей миграции, не зависеть от нее в процессе производства и может адекватно проверить поведение, вы можете эксперимент , как показано ниже.

Обсуждение вашейпопытка

Я показываю, что вы используете неправильный подход : простое приведение указателя к основанию к указателю на производное не изменяет vtable объектауказатель.

Получение вероятного взлома

Обращаясь к этому, наивный подход состоит в том, чтобы реконструировать объект на месте как производный объект («размещение» new), но это не тоже не работает - он инициализирует членов базового класса.

Что вы можете возможно сделать, это создать непроизводный объект, который не имеет членов данных, кроме те же записи таблицы виртуальной отправки (т. Е. Те же виртуальные функции, тот же доступ, частный / защищенный / общедоступный, тот же порядок).

Дополнительные предупреждения и предостережения

Это может работать (как и намой Linux box), но используйте его на свой страх и риск (я предлагаю не в производственных системах).

Дальнейшее предупреждение: это может только перехватить виртуальную диспетчеризацию , ииногда виртуальные функции могут отправляться статически, когда компилятор знает типы во время компиляции.

~/dev cat hack_vtable.cc
// change vtable of existing object to intercept virtual dispatch...

#include <iostream>

struct B
{
    virtual void f() { std::cout << "B::f()\n"; }

    std::string s_;
};

struct D : B
{
    virtual void f() { std::cout << "D::f()\n"; }
};

struct E
{
    virtual void f() { std::cout << "E::f()\n"; }
};

int main()
{
    B* p = new B();
    p->s_ = "hello";
    new (p) D();  // WARNING: reconstructs B members

    p->f();
    std::cout << '\'' << p->s_ << "'\n"; // no longer "hello"

    p->s_ = "world";
    new (p) E();
    p->f();  // correctly calls E::f()
    std::cout << '\'' << p->s_ << "'\n"; // still "world"
}

~/dev try hack_vtable   
make: `hack_vtable' is up to date.
D::f()
''
E::f()
'world'
1 голос
/ 14 апреля 2011

В этом вопросе проблема с понижением в c ++ Ответ Робса также должен быть ответом на вашу проблему.

1 голос
/ 14 апреля 2011

Что ж, даже если вы преобразуете свой базовый объект как производный, он все равно остается базовым: vftable вашего объекта (фактическая карта функций с указателями RAM) не обновляется.Я не думаю, что есть какой-то способ сделать то, что вы хотите, и я не понимаю, почему вы хотите это сделать.

0 голосов
/ 14 апреля 2011

По крайней мере, законным путем. Чтобы вызвать функцию класса Derived, вам нужно иметь ссылку на Derived object.

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