Войти

Показать полную графическую версию : [решено] Виртуальные функции(vtable)


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

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

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

crashtuak
17-08-2013, 17:13
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
Демонстрация 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




© OSzone.net 2001-2012