Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   [решено] Виртуальные функции(vtable) (http://forum.oszone.net/showthread.php?t=266337)

crashtuak 16-08-2013 22:45 2202960

Виртуальные функции(vtable)
 
Добрый день. Вот разбираюсь с виртуальными функциями. На сколько я понял, в начале класса, в котором есть виртуальные методы, делается указатель на таблицу(vtable), в которой хранятся указатели на функции конкретного класса. Теперь вопрос - если класс наследуется от двух других классов с виртуальными методами, то в нем будут хранится две таблицы(по 1 на каждого виртуального предка)? И что бы кастануть наш класс к любому из предков, надо юзать dynamic_cast<>?

pva 17-08-2013 08:41 2203045

Про то, как обычно оно устроено (советую прочитать все лекции - это перевернёт и упорядочит твой мир):
http://www.stanford.edu/class/archiv...2/Slides12.pdf
Про то, для чего и что может dynamic_cast (имхо лучше им не злоупотреблять):
http://msdn.microsoft.com/en-us/library/cby9kycs.aspx

Как правило любой dynamic_cast начинается с прыжка в базовый класс, а дальше поиск кандидата по всем дочерним классам.

crashtuak 17-08-2013 17:13 2203240

pva, спасибо, чтиво интереснейшее. Сам я работаю на Java, и для меня привычно, когда абсолютно все что можно, скрыто за интерфейсами, и простым кастом можно привести объект наследника к одному из нескольких интерфейсов. А в с++, как оказалось, не все так просто:). Вот что получилось у меня:
Код:

class i1
{
  virtual m1() = 0;
};
class i2
{
  virtual m2() = 0;
};
class impl : public i1, public m2
{
  //implementation of i1,i2
}
//main
impl* implInst = new impl();
i2* i2Cast = (i2*)implInst;

И каково было мое удивление, когда вызывался m1() :). Как оказалось, компилятор под видом i2 принял i1(который в impl первый в памяти), обратился к i1_vtable, и вызвал m1(). И, кстати, при передаче наследника в методы вида void doSomething(i2*) генерируется код, идентичный для dynamic_cast<>, так что, скорее всего, избежать dynamic_cast<> не выйдет:). Для примера
Код:

        Action* v = dynamic_cast<Action*>(с);
00E93BEF  cmp        dword ptr [ff],0 
00E93BF3  je          wmain+143h (0E93C03h) 
00E93BF5  mov        eax,dword ptr [ff] 
00E93BF8  add        eax,4 
00E93BFB  mov        dword ptr [ebp-130h],eax 
00E93C01  jmp        wmain+14Dh (0E93C0Dh) 
00E93C03  mov        dword ptr [ebp-130h],0 
00E93C0D  mov        ecx,dword ptr [ebp-130h] 
00E93C13  mov        dword ptr [v],ecx 
        doAction(ff);
00E93C16  cmp        dword ptr [ff],0 
00E93C1A  je          wmain+16Ah (0E93C2Ah) 
00E93C1C  mov        eax,dword ptr [ff] 
00E93C1F  add        eax,4 
00E93C22  mov        dword ptr [ebp-130h],eax 
00E93C28  jmp        wmain+174h (0E93C34h) 
00E93C2A  mov        dword ptr [ebp-130h],0 
00E93C34  mov        ecx,dword ptr [ebp-130h] 
00E93C3A  push        ecx 
00E93C3B  call        doAction (0E91226h) 
00E93C40  add        esp,4

где ff реализует Action, возможно, что то поменялось бы с оптимизациями(код дебажный), но я не думаю, что одинаковый код можно по разному оптимизировать :).
UPD: хотя, скорее всего, в других ситуациях dynamic_cast выдаст более навороченый код, чем в данном случае.

pva 18-08-2013 00:24 2203401

Демонстрация static_cast, dynamic_cast
Код:

class i1 {
public:
        virtual void foo() = 0;
};

class i2 {
public:
        virtual void bar() = 0;
};

class i3 {
public:
        virtual void goo() = 0;
};

class i4 {
public:
        virtual void poo() = 0;
};

class impl: public i3, public i1, public i2 {
public:
        void goo() { cout << "goo()" << endl; }
        void foo() { cout << "foo()" << endl; }
        void bar() { cout << "bar()" << endl; }
};

int main() {
        impl *impl_ = new impl();
        i1 *i1_ = impl_;
        i2 *i2_ = impl_;
        i3 *i3_ = impl_;
        i1 *side_cast = dynamic_cast<i1*>(i2_); // статически не наследуются
        i4 *bad_cast = dynamic_cast<i4*>(i2_); // статически не наследуются

        i1_->foo();
        i2_->bar();
        side_cast->foo();

        cout << "impl=" << impl_ << endl;
        cout << "i1=" << i1_ << endl;
        cout << "i2=" << i2_ << endl;
        cout << "i3=" << i3_ << endl;
        cout << "side_cast=" << side_cast << endl;
        cout << "bad_cast=" << bad_cast << endl;

        return 0;
}

Код:

foo()
bar()
foo()
impl=0x3e1030
i1=0x3e1034
i2=0x3e1038
i3=0x3e1030
side_cast=0x3e1034
bad_cast=0



Время: 04:47.

Время: 04:47.
© OSzone.net 2001-