Динамическое изменение виртуального указателя во время выполнения - PullRequest
4 голосов
/ 06 ноября 2011

Итак, скажем, у меня есть два класса, которые наследуют базовый класс, который имеет чисто виртуальную функцию. Оба класса реализуют свою собственную версию этой функции, но не добавляют дополнительных переменных-членов, поэтому они имеют одинаковый размер. Теперь иногда, во время выполнения программы, я хочу преобразовать один класс в другой, не копируя все его данные. В общем, я хочу, чтобы он использовал виртуальную таблицу другого класса. Есть ли портативный способ сделать это?

Ответы [ 7 ]

5 голосов
/ 06 ноября 2011

Портативный способ сделать это - реализовать собственную систему классов, в которой фактически есть виртуальные указатели, которые можно копировать.

В стандарте C ++ не существует такого понятия, как виртуальный указатель.

4 голосов
/ 06 ноября 2011

Один молодой коллега из Andersen Consulting (сейчас Accenture) в Норвегии однажды обратился ко мне с серьезной проблемой. Их приложения, разработанные в Visual Basic, загружались очень долго. Он подозревал, что это может быть потому, что они помещают каждый класс в свою собственную DLL?

Опасаясь худшего, я поинтересовался дальше. И да, у них также были проблемы с произвольными сбоями и т. Д.

Он подозревал, что в противном случае необъяснимые сбои могут быть связаны с их оригинальной схемой изменения типа объекта во время выполнения путем замены указателя vtable?

Я предположил, что, может быть, они не должны делать такие вещи. Он скептически посмотрел на меня и решился, что у них не было времени снова что-то делать с нуля. Фактически, они уже растягивали это, и с этим возникали различные проблемы, например, руководитель проекта настаивал на том, чтобы они работали на площадке клиента, а не участвовали в обязательных встречах. Для меня это звучало как управление грибами (держите их в темноте, когда голова всплывает, отрежьте ее): эти вещи часто идут вместе.

В любом случае, я даю вам тот же совет: не надо.

Возможно, вы можете вместо этого реализовать быстрые операции перемещения для перемещения данных из a в b ?

Или, возможно, вы обнаружите, что все это случай преждевременной оптимизации ?

Приветствия и hth.,

4 голосов
/ 06 ноября 2011

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

Некоторая форма композиции, вероятно, больше подходит для вашей задачи.

3 голосов
/ 06 ноября 2011

Как уже говорилось в других ответах, изменение виртуальной таблицы на самом деле непереносимо.

Однако есть несколько обходных путей, которые могут позволить вам выполнить аналогичную семантику без фактического изменения типа класса:

Это простейшее решение - «свернуть свое» наследование с помощью перечисления, описывающего текущую реализацию:

class MyClass
{
    public:
    enum DerivedType { A, B };

    private:
    DerivedType myType;

    public:
    void myVirtualFunction()
    {
        if (myType == A)
            myAFunction();
        else
            myBFunction();
    }
}

Вы также можете использовать указатель функции в качестве открытой переменной-члена, для которой установлено значениефункция, указывающая тип класса.Тогда вы могли бы установить указатель на функцию другого класса, чтобы «изменить его тип»

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

3 голосов
/ 06 ноября 2011

Есть ли портативный способ сделать это?

Абсолютно нет. Специфика реализации виртуальных функций не определяется спецификацией, и поэтому нет переносимого способа притвориться одним виртуальным классом другим.

1 голос
/ 22 апреля 2019

А как насчет размещения новых? Возможно, это не совсем переносимо, но оно делает именно то, что нужно - заменяет vtable и ничего более. Просто нужно позаботиться о конструкторе - используйте пустой.

struct Base
{
    int someData;
    virtual int GetValue() = 0;
};

struct A : public Base
{
    int GetValue() override { return 11111; }
};

struct B : public Base
{
    int GetValue() override { return 22222; }
};

A ob;
ob.someData = 123;
auto ob2 = new (&ob) B;
auto value = ob2->GetValue();

Не упоминая очевидные вещи, такие как размер классов, лучшие практики и т. Д.

0 голосов
/ 01 декабря 2015

Хотя этот вопрос старый, я бы хотел найти способ сделать это.(Не совсем уверен в переносимости)

Насколько я понимаю, у вас есть классы B и C, которые наследуются от некоторого класса A, и между ними существует только одна виртуальная функция.(Метод, который я здесь представляю, работает, если B и C также не связаны между собой.)

class A {
public:
    virtual std::string hello() = 0;
};

class B : public A { 
public:
    virtual std::string hello() { return "B"; }
};

class C : public A {
public:
    virtual std::string hello() { return "C"; }
};

И затем вы хотите взять B в C, затем вызвать hello и получите "B".


Итак, есть способ создать разбавленную версию boost::any, которая будет преобразовывать что угодно во все, что угодно:)

struct parent {};

template< typename T >
struct child : public parent {
    child(T const& t): item(t){}
    mutable T item;
};

template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }

Затем смешатьвсе вместе:

B b;
parent* p = new child< B >(b);
std::cout << as< C >(*p).hello() << std::endl;
// ==== OUTPUT ====
// B

Можно увидеть код в действии здесь .


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

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    parent* p = new child< FROM >(from);
    return as< TO >(p);
};

Это можно запустить здесь .

(Понял, что я пропустилнаследование в этом примере ссылки на код, но после прочтения вопроса я думаю, что это действительно то, что было на самом деле желательно. Итак, чтобы посмотреть тест без наследования, перейдите здесь )


Какой-то другой кодчто я начал играть с этим, я подумал, что может помочь и некоторым ...

#include <iostream>
#include <string>

class B {
public:
    virtual char hello() {return 'B';}
};

class C {
public:
    virtual int hello() {return 65;}
};

struct parent {};

template< typename T >
struct child : public parent {
    child(T const& t): item(t){}
    mutable T item;
};

template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    parent* p = new child< FROM >(from);
    return as< TO >(*p);
};

int main()
{
    B b;
    std::cout << convert< C, B >(b).hello() << std::endl;
    C c;
    std::cout << convert< B, C >(c).hello() << std::endl;
}
// ==== OUTPUT ====
// 66
// A

Разобраться, как сделать все это в функции преобразования:

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    struct parent {};

    struct child : public parent {
        child(FROM const& t): item(t){}
        mutable FROM item;
    };

    struct sibling : public parent {
        sibling(TO const& t): item(t){}
        mutable TO item;
    };

    parent* p = new child(from);
    return static_cast< sibling const& >(*p).item;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...