Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Как получить тип через указатель на базовый класс?


Автор: georain 20.12.2007, 01:43
Подскажите, что то я запутался.

Есть дерево полиморфных классов, есть указатель на базовый класс.
Как я могу (и могу ли вообще) получить указатель на класс, на который указывает указатель на базовый класс.

 smile 

Автор: Daevaorn 20.12.2007, 01:55
"Указателей на класс" в С++ не существует.

Автор: georain 20.12.2007, 02:09
Извините, неправильно (сокращённо) выразился.

Указатель с типом производного класса?

Код

Base * b_ptr;
DerivedA * d_ptrA;
DerivedB * d_ptrB;
DerivedC * d_ptrC;


Нужно получить d_ptrA, d_ptrB или d_ptrС каким-либо образом из b_ptr.

Автор: dizzy1984 20.12.2007, 07:25
Если b_ptr указывает на DerivedA, то можно так
Код

d_ptrA = dynamic_cast <DerivedA *>(b_ptr)

или с помощью c-cast
Код

d_ptrA = (DerivedA *)b_ptr;

Автор: georain 20.12.2007, 07:45
Так это понятно  smile

А если мы заранее не знаем что он указывает на DerivedA?
Мы может определить это через специальную виртуальную функцию.
Но как это автоматизировать?

Хочется что-то типа
b_ptr = dynamic_cast <(b_ptr->get_my_type)>(b_ptr)  smile 
только без RTTI, ну и чтоб работало)

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

Автор: dizzy1984 20.12.2007, 09:50
А можно привести ситуацию в которой такое преобразование необходимо?

Автор: MAKCim 20.12.2007, 10:16
Цитата(georain @  20.12.2007,  07:45 Найти цитируемый пост)
Хочется что-то типа
b_ptr = dynamic_cast <(b_ptr->get_my_type)>(b_ptr)  smile 
только без RTTI, ну и чтоб работало)

нельзя
в С++ статическая типизация

Автор: Alek86 20.12.2007, 11:01
georain, а можно поинтересоваться, зачем это тебе нужно?
по-моему, ты пренебрегаешь такой хорошей штукой, как полиморфизм

скорее всего, твоя проблема решается с помощью виртуальных функций.

Автор: MAKCim 20.12.2007, 11:58
 smile 
Alek86, 
dizzy1984, 
я вот не понимаю, зачем задавать подобные вопросы
это самый что ни на есть прямой уход от поставленной задачи
что не есть хорошо
человек определенно спросил, можно ли, дескать, сделать так-то и так-то
а вы начинаете демогогию типа "а зачем тебе это нужно"
не знаете - не отвечайте
извините за резкость...накипело


Автор: baldina 20.12.2007, 12:00
Цитата

только без RTTI, ну и чтоб работало)


без RTTI вообще никак (ну только если ты сам не хранишь поле типа). RTTI нужен для dynamic_cast.

Цитата

скорее всего, твоя проблема решается с помощью виртуальных функций. 


полностью согласен.

Цитата

Хочется что-то типа
b_ptr = dynamic_cast <(b_ptr->get_my_type)>(b_ptr) 


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


Цитата

Base * b_ptr;
DerivedA * d_ptrA;
DerivedB * d_ptrB;
DerivedC * d_ptrC;

Нужно получить d_ptrA, d_ptrB или d_ptrС каким-либо образом из b_ptr.

Это в smalltalk такой подход. Мы не знаем, чем на самом деле является класс и запрашиваем возможность использования его в качестве одного из подтипов. Если получается - используем. Для этого как раз может подойти dynamic_cast.

Например, у тебя в массиве хранятся указатели на объекты типа Base, но реально имеющие производный тип. И ты хочешь над всеми, кто указывает на DerivedA, выполнить специфичную для DerivedA (не полиморфную) операцию. Будет так:
Код

std::vector<Base*> b_ptrs;
for (int i=0; i < b_ptrs.size(); ++i)
{
  DerivedA* d_ptrA = dynamic_cast<DerivedA*>(b_ptrs[i]);
  if (d_ptrA != 0)
  {
     // ok, b_ptrs[i] указывает на DerivedA
     d_ptrA->some_func();
  }
}


Автор: MAKCim 20.12.2007, 12:07
Цитата(baldina @  20.12.2007,  12:00 Найти цитируемый пост)
сам то понял что сказал? у тебя уже есть b_ptr. А если ты хочешь и можешь использовать его полиморфно, приведение типа не нужно.

я отлично понял, что имел в виду georain
и объяснил, что в С++ такое невозможно 

Автор: Alek86 20.12.2007, 12:24
оффтоп

Цитата(MAKCim @  20.12.2007,  11:58 Найти цитируемый пост)
  Alek86, dizzy1984, я вот не понимаю, зачем задавать подобные вопросы


я "отвечал" не на первый вопрос темы (на него уже ответили) а на этот:

Цитата(georain @  20.12.2007,  07:45 Найти цитируемый пост)
Хочется что-то типаb_ptr = dynamic_cast <(b_ptr->get_my_type)>(b_ptr)   только без RTTI, ну и чтоб работало)



пытался, так сказать, как Христос, не накормить рыбой а научить ловить smile

Автор: baldina 20.12.2007, 12:31
Цитата

я отлично понял, что имел в виду georain

MAKCim, это автору предназначалось.

Автор: MAKCim 20.12.2007, 12:40
Цитата(baldina @  20.12.2007,  12:31 Найти цитируемый пост)
MAKCim, это автору предназначалось. 

 smile 
я думаю, если другие поняли, то и автор должен понимать

Автор: georain 20.12.2007, 13:52
Ладно, объясняю зачем мне это нужно. Всё опять же нужно для оптимизации по скорости. Возможно есть какой-то другой способ решения, если знаете подскажите пожалуйста.

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

Т.е. что-то типа
Код

class Derived: public Base
{
inline virtual void func();
}

ptr * Base = derived;

/*каким-то магическим способом получаем D_ptr с типом Derived*/

(*D_ptr)->func() //здесь происходить прямой вызов или даже подстановка 



MAKCim, спасибо за понимание. Я пока писал понял что действительно в с++ так нельзя... если без шаблонов...))

baldina, с dynamic_cast пришлось бы делать перебор для всех классов, а он сам по себе очень не дешёвый - в итоге смысла нет.

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

Автор: Alek86 20.12.2007, 14:01
то есть, если я правильно понял, ты хочешь вызывать функции не виртуально (через таблицу виртуальных функций) а напрямую?
вообще избавиться от виртуальности функций?

Автор: MAKCim 20.12.2007, 14:04
georain, 
а если так сделать
Код

class A {
private:
    void __a1() { ... }
    void __a2() { ... }
...
    void __an() { ... }
public:
    virtual void a1() { __a1(); }
    virtual void a2() { __a2(); }
...
    virtual void an() { __an(); }
    virtual void combination(int flags) {
         // тут вызывать __a1(), __a2(), ... напрямую
    }
};

Автор: Alek86 20.12.2007, 14:08
исправьте меня, если ошибусь, но, вроде, единственные издержки в вызове виртуальных функций - это то, что они вызываются через указатели, и ускорить их вызов можно, лишь сделав их невиртуальными...

Добавлено через 29 секунд
Цитата(MAKCim @  20.12.2007,  14:04 Найти цитируемый пост)
а если так сделать


Цитата(georain @  20.12.2007,  13:52 Найти цитируемый пост)
Конечно есть вариант в самих классах определять дополнительные виртуальные функции с необходимой последовательностью, но их там и так много, перебор получается, к тому-же это сильно увеличит размер виртуальных таблиц.


Автор: MAKCim 20.12.2007, 14:23
Alek86, 
Код

class A;

typedef void (*callback_t)(A * const);

class A {
private:
    void __a1() { ... }
    void __a2() { ... }
...
    void __an() { ... }

    static callback_t __array[NR_COMBINATION];

    static void combination1(A * const ptr) {
        // последовательность вызовов 1
    }

    static void combination2(A * const ptr) {
        // последовательность вызовов 2
    }
...
    static void combinationn(A * const ptr) {
        // последовательность вызовов NR_COMBINATION
    }
public:
    virtual void a1() { __a1(); }
    virtual void a2() { __a2(); }
...
    virtual void an() { __an(); }
    virtual void combination(int number) {
         __array[number](this);
    }
};

callback_t A :: __array[NR_COMBINATION] = {
    &A :: combination1,
    &A :: combination2,
...
    &A :: combinationn 
};

Автор: Alek86 20.12.2007, 14:30
тогда по вопросу осталась мелочь - побыстрее определить, что текущий указатель указывает именно на A, а не на B и вызвать определенную статическую функцию с кастанутым текущим указателем...

может, typeof поможет

Автор: HappyLife 20.12.2007, 14:37
Кхм.... интересно а насколько дорого стоит вызов виртуальной функции?

Автор: Lazin 20.12.2007, 14:37
Не знаю насколько я попал в тему...
избавиться от дополнительных виртуальных ф-й и вызывать правильные не виртуальные методы в производных классах позволяет шаблон проектирования Curiously recurring template

Автор: MAKCim 20.12.2007, 15:19
Цитата(HappyLife @  20.12.2007,  14:37 Найти цитируемый пост)
Кхм.... интересно а насколько дорого стоит вызов виртуальной функции? 

Код

    mov    eax, dword [edx]
    push    edx
    call    dword [eax + OFFSET]

вместо
Код

    push    edx
    call    <displacement>

Автор: Alek86 20.12.2007, 15:35
ну, и еще ее нельзя инлайнить, в общем случае

Автор: Earnest 25.12.2007, 18:12
Цитата(Lazin @  20.12.2007,  15:37 Найти цитируемый пост)
избавиться от дополнительных виртуальных ф-й и вызывать правильные не виртуальные методы в производных классах позволяет шаблон проектирования Curiously recurring template 

Строго говоря, вовсе необязательно CRT - любой шаблон обеспечивает статический полиморфизм. Только не всегда можно заменить динамический полиморфизм статическим - для этого нужно знать типы на этапе компиляции.

Автору: большое дерево классов + много виртуальных функций - уже надо что-то в консерватории поправить. Если достаточно определить в одном месте тип, а потом можно вызвать конкретную функцию, так и сделай именно эту функцию виртуальной (а остальные - невиртуальными). Замена на callback, как вроде кто-то предлагал - хрень редьки не слаще, тот же косвенный вызов с теми же издержками. 

Кстати, они действительно небольшие. Ты уверен, что стОит париться, т.е. узкое место с производительностью именно здесь? Профилятор это показал? Или ты просто так думаешь, заранее?

Автор: baldina 25.12.2007, 18:18
Цитата

Строго говоря, вовсе необязательно CRT - любой шаблон обеспечивает статический полиморфизм. Только не всегда можно заменить динамический полиморфизм статическим - для этого нужно знать типы на этапе компиляции.

smile

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

Автор: georain 25.12.2007, 19:01
В общем я сделал все виртуальные функции обычными, и запихал все свои комбинации в новые виртуальные функции, в итоге добился того что и хотел: вызов только одно виртуальной функции для комбинации. В итоге выиграл 4% производительности. Я считаю что это прилично, и труд был потрачен не зря.

На счет моего вопроса: для решения таких проблем нужно очень хорошо представлять, что уже известно на этапе компиляции, а что нет. Задавая вопрос "Как я могу (и могу ли вообще) получить указатель на класс, на который указывает указатель на базовый класс?", я допустил логическую ошибку. MAKCim уже сказал, что не могу, потому что (как сказал Earnest) узнать на какой класс указывает базовый указатель мы может только на этапе выполнения, а связывание по типу происходит на этапе компиляции (например вызов нужной функции, в зависимости от типа указателя).

Спасибо всем за огромную помощь, как говорится, дурная голова никому покоя не дает.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)