Легенда:
новое сообщение
закрытая нитка
новое сообщение
в закрытой нитке
старое сообщение
|
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
- Новичкам также крайне полезно ознакомиться с данным документом.
| | | | | | | |
Давай разбираться... 09.01.02 14:21 Число просмотров: 1760
Автор: dl <Dmitry Leonov> Отредактировано 09.01.02 14:22 Количество правок: 1
|
> > Причем забавно, что проблем при выполнении нет и в > > последнем, не совсем корректном примере даже тогда, > когда в > > f1 используется какое-нибудь поле из производного > класса - > > по крайней мере, в простых примерах. > > Ну стек тут ни причем - в этом примере объект не на стеке > создается, а выделяется из хипа.
Да, про стек я уже поправил :)
И наверное поскольку
> память выделяется из хипа блоками, на практике выделяется > немного больше, чем нужно классу. Только из-за этого можно > обращаться к некоторым полям (напр. к DWORD). А если в > классе объявить массив на 100 Кб и обратиться к нему - > прога упадет на 100%. > И так, резюме: > 1) В D::f2() нельзя обращаться к данным > класса D, которые не унаследованы из > класса B, потому что их там просто нет.
Формально нельзя, но иногда это прощается.
> 2) Еще одна причина некорректности последнего примера (а ты > об этом рассказываешь студентам на лабах? :-) > если B::f2() сделать не виртуальной, то > прога упадет
Кхм, так f1 невиртуальная, и ничего не падает :) Хотя понятно, что это некорректный код.
|
<programming>
|
[C++] Kak bam etot primer ... ? 28.12.01 05:07
Автор: + <Mikhail> Статус: Elderman
|
class B
{
public:
void f();
virtual void vf()
{
printf("B::vf()\n");
};
};
class A : public B
{
public:
void f()
{
printf("A::f()\n");
};
void vf()
{
printf("A::vf()\n");
};
};
void B::f()
{
((A*)this)->f();
((A*)this)->vf();
};
int main()
{
B *pb = new B();
pb->f();
delete pb;
return 0;
}
---
|
|
И что? 28.12.01 14:04
Автор: Rook <Alex Sergeev> Статус: Member
|
|
| |
A to chto soglasno etomu primeru vse funcii v classe static... 28.12.01 21:36
Автор: + <Mikhail> Статус: Elderman
|
dazhe v sluchiae s virtual foo oni static, tolko konechno zhe vpt ne inicializirovana potomuchto vitual ne mozhet byt` static.
|
| | |
Давай разберемся 08.01.02 09:15
Автор: :-) <:-)> Статус: Elderman Отредактировано 08.01.02 09:16 Количество правок: 1
|
> dazhe v sluchiae s virtual foo oni static, tolko konechno > zhe vpt ne inicializirovana potomuchto vitual ne mozhet > byt` static.
;-)))
Пример печатает:
A::f()
B::vf()
A::vf() и B::vf() - виртуальные (иначе бы во второй строке печаталось A::vf()). Остальные ф-ии невиртуальные.
Так все и должно быть.
Но вот эти 2 строчки у тебя кривые:
((A*)this)->f();
((A*)this)->vf(); ---
Ты приводишь класс B (базовый) к классу A (производному), а этого делать нельзя. Производный класс можно приводить к базовому, но не наоборот!
Происходит вот что:
((A*)this)->f(); ---Вызывается A::f() (точнее ее тело встраивается в код в место вызова, потому что она inline и не виртуальная), но при этом в качестве указателя this на свой класс A ей передается указатель на базовый класс B ;-))
Этот код пока работает только потому, что A::f() не обращается к данным своего класса (поэтому сейчас для A::f() вообще пофигу, что ей передают в this, хоть NULL: ((A*)NULL)->f(); - можно ее и так вызвать :-)
((A*)this)->vf(); ---A::vf() - виртуальная, и компилятор генерит ее вызов через VTBL. А так как this всегда указывает на класс B, все равно будет вызвана B::vf(), что собственно и происходит.
Приведение базового B* к производному A* - это ошибка, и если в классе B не будет виртуальной B::vf(), прога на этой строчке упадет (OS ее порвет).
Так что ничего странного в этом примере нет, конечно если не считать того, что код кривоват :-)
|
| | | |
Давай... 09.01.02 03:18
Автор: + <Mikhail> Статус: Elderman Отредактировано 09.01.02 04:12 Количество правок: 1
|
> > dazhe v sluchiae s virtual foo oni static, tolko > konechno > > zhe vpt ne inicializirovana potomuchto vitual ne > mozhet > > byt` static. > > ;-))) > Пример печатает: > A::f() > B::vf() > > A::vf() и B::vf() - виртуальные (иначе бы во второй строке > печаталось A::vf()). Остальные ф-ии невиртуальные. > Так все и должно быть. > > Но вот эти 2 строчки у тебя кривые: > ((A*)this)->f(); > ((A*)this)->vf(); ---
Nu i gde zhe krivizna??
> > Ты приводишь класс B (базовый) к классу > A (производному), а этого делать нельзя.
Vo pervyh kto skazal chto eto delat` nelzia? (eto delat` mozhno tolko na do uchityvat` chto object typa derived class mozhet ne suchestvovat`, chto ochen` horosho vidno is promera)
Ezhe raz govoru: etot code pokazyvaet chto function is static.
> Производный класс можно приводить к базовому, но не > наоборот!
Deistvitelno? na kakom ty kurse? ili ty eche v shkole?
Est` takoi termin: downcast-
The process of converting a base class pointer or reference to a derived class pointer or reference. ( specialno privel opredelenie , a ne napisal svoimi slovami )
> Происходит вот что: > ((A*)this)->f(); ---Вызывается > A::f() (точнее ее тело встраивается в > код в место вызова, потому что она inline и не
gde ty vidish chto ona "inline"??
> виртуальная), но при этом в качестве указателя this на свой > класс A ей передается указатель на > базовый класс B ;-)) > Этот код пока > работает только потому, что A::f() не
Prichem sdes` obrachenie k dannym svoego klassa kogda function is static (sm opredelenie static member function), static foo tem i otlichaetsi ot non static foo chto eii mozhno polzovat`sia ne sozdavaia objecta, i estestvenno ni o kakom obrachenii k data member i rechi byt ne mozhet.
> обращается к данным своего класса (поэтому сейчас для > A::f() вообще пофигу, что ей передают в > this, хоть NULL: ((A*)NULL)->f(); - можно ее и так > вызвать :-)
Bot ty i pokazal chto A::f() is static function
> > ((A*)this)->vf(); ---A::vf() > - виртуальная, и компилятор генерит ее вызов > через VTBL. А так как this всегда > указывает на класс B, все равно будет > вызвана B::vf(), что собственно и > происходит. > Приведение базового B* к производному > A* - это ошибка, и если в классе > B не будет виртуальной > B::vf(), прога на этой строчке упадет > (OS ее порвет). > > Так что ничего странного в этом примере нет, конечно если > не считать того, что код кривоват :-)
I eche raz govoru o tom chto iz moego primera vidno chto compiler realizovan takim obrazom (i ispolzoval VC, naverno i na drigih tozhe samoe...) chto vse non virtual function static(potomuchto dlia virtual foo, vtbl ne inicializirovana kogda object ne sozdan)
Nu dak ochem spor to byl?? Chto to I ne ponial?
|
| | | | |
Давай... 09.01.02 09:58
Автор: :-) <:-)> Статус: Elderman Отредактировано 09.01.02 10:03 Количество правок: 5
|
> Vo pervyh kto skazal chto eto delat` nelzia? (eto delat` > mozhno tolko na do uchityvat` chto object typa derived > class mozhet ne suchestvovat`, chto ochen` horosho vidno is > promera) > Ezhe raz govoru: etot code pokazyvaet chto function is > static.
Да с чего ты взял, что они static? static-функциям this вообще не передается, именно поэтому они не могут обращаться к данным своего класса. А эти ф-ии - обычные, им передается this, и они могут обращаться к данным своего класса.
> Производный класс можно приводить к базовому, но не > наоборот! > Deistvitelno? na kakom ty kurse? ili ty eche v shkole? В детский сад пока ходим, млин. А ты ? :-)
> Est` takoi termin: downcast > The process of converting a base class pointer or reference > to a derived class pointer or reference. ( specialno > privel opredelenie , a ne napisal svoimi slovami )
Назывется слышал звон, да не знаешь для чего он :-)
downcast можно делать только если ты знаешь, что объект, на который указывает указатель базового класса, действительно является объектом производного класса. Тогда ест-но можно преобразовать этот указатель к производному классу. А тут сосвем наоборот: ты знаешь, что объект базового класса.
Сам подумай: в производном классе могут быть объявлены данные, которых нет в базовом классе. А ты вызываешь ф-ю производного класса, передавая ей в this указатель на объект базового класса. Как же ф-я производного класса будет обращаться к своим данным? :-))
Да просто объяви в классе A
char m[100000];
А в ф-ю A::f() добавь строчку memset(m, 0, sizeof(m));
и увидишь, как прога упадет.
Заметь, если бы A::f() была static, то комплятор бы этого даже не скомпилировал (static ф-я не может обращаться к данным своего класса через this)
> > код в место вызова, потому что она inline и не > gde ty vidish chto ona "inline"??
Потому что она определена в теле класса.
> I eche raz govoru o tom chto iz moego primera vidno chto > compiler realizovan takim obrazom (i ispolzoval VC, naverno > i na drigih tozhe samoe...) chto vse non virtual function > static(potomuchto dlia virtual foo, vtbl > ne inicializirovana kogda object ne sozdan) >
Раз ты не объявил их с модификатором static, они не могут быть таковыми. Компилятор реализует вызов невиртуальных ф-й также, как и глобальных/static ф-й, но в отличие от последних передает им в this указатель на объект класса, для которого она вызвана (для MSVC это обычно регистр ecx):
pb->f();
генерится :
mov ecx, pb
call B::f
|
| | | | |
Давай разбираться... 09.01.02 05:00
Автор: Biasha <Бяша> Статус: Member Отредактировано 10.01.02 09:44 Количество правок: 11
|
> > Но вот эти 2 строчки у тебя кривые:
((A*)this)->f();
((A*)this)->vf();
---
> Nu i gde zhe krivizna?? Кривизна в преобразовании типа об'єкта к тому, чем он не является.
И вообще кривизна - вещь субъективная, для меня преобразование типа - почти всегда кривизна. Так что это не важно.
> Ezhe raz govoru: etot code pokazyvaet chto function is > static.
> > Происходит вот что:
((A*)this)->f(); ---
> > Вызывается > > A::f() (точнее ее тело встраивается > в > > код в место вызова, потому что она inline и не > gde ty vidish chto ona "inline"?? Чистая правда - inline. Функции класса, реализованные в определёнии - по умолчанию inline.
> > Так что ничего странного в этом примере нет, конечно > > если > > не считать того, что код кривоват :-) > I eche raz govoru o tom chto iz moego primera vidno chto > compiler realizovan takim obrazom (i ispolzoval VC, naverno > i na drigih tozhe samoe...) chto vse non virtual function > static(potomuchto dlia virtual foo, vtbl > ne inicializirovana kogda object ne sozdan)
> Nu dak ochem spor to byl?? Chto to I ne ponial?
Вот я тоже.
Давай разберёмся. Ты говоришь, что все функции класса - static. Что ты понимаешь под этим?
Лично я понимаю под static некоторое свойство функции, которое использует только компилятор, которое помогает думать программисту. С этой точки зрения - эти функции вовсе не статик - ведь они не обладают, например, тем свойством, что можно получить их адрес непосредственно.
void B::f()
{
void *a =((A*)this)->f;
((A*)this)->f();
((A*)this)->vf();
}; ---
Это демонстрирует. Если объявить A::f как static всё компилируется.
А если пример для демонстрации обхода компилятора - так зачем это нужно, он ведь для нас старается. Тогда и переменные все не const - можно ж писать куда угодно, при необходимости сняв защиту со страницы памяти.
|
| | | | | |
Давай разбираться... 09.01.02 13:12
Автор: dl <Dmitry Leonov> Отредактировано 09.01.02 13:16 Количество правок: 2
|
Ребята, а о чем спор? У меня абсолютно аналогичный код используется в лабе по исследованию поведения виртуальных функций - разве что приводится не this, а внешние указатели, но сути дела оно не меняет.
Ну да, обман компилятора, обычное следствие ручного приведения. Хотелось бы аккуратности - использовался бы dynamic_cast.
Кстати, и это не делает функцию f1 статической, поскольку она продолжает получать указатель this.
Что-то типа такого
#include <iostream.h>
class B
{
public:
void f1(){cout << "B::simple"<<endl;}
virtual void f2(){cout << "B::virtual"<<endl;}
};
class D: public B
{
public:
void f1(){cout << "D::simple"<<endl;}
virtual void f2(){cout << "D::virtual"<<endl;}
};
void main()
{
B* b = new B;
D* d = new D;
B* bd = new D;
D* db1 = (D*)bd;
D* db2 = (D*)b;
b->f1();
b->f2();
d->f1();
d->f2();
bd->f1();
bd->f2();
db1->f1();
db1->f2();
db2->f1();
db2->f2();
}
---
Причем забавно, что проблем при выполнении нет и в последнем, не совсем корректном примере даже тогда, когда в f1 используется какое-нибудь поле из производного класса - по крайней мере, в простых примерах.
|
| | | | | | |
Давай разбираться... 09.01.02 14:12
Автор: :-) <:-)> Статус: Elderman
|
> Причем забавно, что проблем при выполнении нет и в > последнем, не совсем корректном примере даже тогда, когда в > f1 используется какое-нибудь поле из производного класса - > по крайней мере, в простых примерах.
Ну стек тут ни причем - в этом примере объект не на стеке создается, а выделяется из хипа. И наверное поскольку память выделяется из хипа блоками, на практике выделяется немного больше, чем нужно классу. Только из-за этого можно обращаться к некоторым полям (напр. к DWORD). А если в классе объявить массив на 100 Кб и обратиться к нему - прога упадет на 100%.
И так, резюме:
1) В D::f2() нельзя обращаться к данным класса D, которые не унаследованы из класса B, потому что их там просто нет.
2) Еще одна причина некорректности последнего примера (а ты об этом рассказываешь студентам на лабах? :-)
если B::f2() сделать не виртуальной, то прога упадет
|
| | | | | | | |
Давай разбираться... 09.01.02 14:21
Автор: dl <Dmitry Leonov> Отредактировано 09.01.02 14:22 Количество правок: 1
|
> > Причем забавно, что проблем при выполнении нет и в > > последнем, не совсем корректном примере даже тогда, > когда в > > f1 используется какое-нибудь поле из производного > класса - > > по крайней мере, в простых примерах. > > Ну стек тут ни причем - в этом примере объект не на стеке > создается, а выделяется из хипа.
Да, про стек я уже поправил :)
И наверное поскольку
> память выделяется из хипа блоками, на практике выделяется > немного больше, чем нужно классу. Только из-за этого можно > обращаться к некоторым полям (напр. к DWORD). А если в > классе объявить массив на 100 Кб и обратиться к нему - > прога упадет на 100%. > И так, резюме: > 1) В D::f2() нельзя обращаться к данным > класса D, которые не унаследованы из > класса B, потому что их там просто нет.
Формально нельзя, но иногда это прощается.
> 2) Еще одна причина некорректности последнего примера (а ты > об этом рассказываешь студентам на лабах? :-) > если B::f2() сделать не виртуальной, то > прога упадет
Кхм, так f1 невиртуальная, и ничего не падает :) Хотя понятно, что это некорректный код.
|
| | | | | | | | |
Давай разбираться... 09.01.02 14:53
Автор: :-) <:-)> Статус: Elderman Отредактировано 09.01.02 16:10 Количество правок: 2
|
> > 2) Еще одна причина некорректности последнего примера > (а ты > > об этом рассказываешь студентам на лабах? :-) > > если B::f2() сделать не > виртуальной, то > > прога упадет > > Кхм, так f1 невиртуальная, и ничего не падает :) Хотя > понятно, что это некорректный код.
Дык при вызове f1 генерится прямой CALL f1.
А вот при db2->f2();
генерится вызов через VTBL (потому что db2 типа D*). Но db2 указывает на класс B, в котором вообще нет VTBL (если f2 сделать невиртуальной). Вот поэтому она и упадет :-))
|
| | | | | | | | | |
Давай разбираться... 09.01.02 17:02
Автор: dl <Dmitry Leonov>
|
> Дык при вызове f1 генерится прямой CALL f1. > А вот при db2->f2(); > генерится вызов через VTBL (потому что db2 типа D*). Но db2 > указывает на класс B, в котором вообще нет VTBL (если f2 > сделать невиртуальной). Вот поэтому она и упадет :-))
Мне ж не жалко было и проверить :) По-прежнему работает, зараза, хотя и не должно :))
#include <iostream.h>
class B
{
public:
void f1(){cout << "B::simple"<<endl;}
void f2(){cout << "B::virtual"<<endl;}
};
class D: public B
{
public:
int i;
void f1(){i = 12345; cout << "D::simple " << i <<endl;}
void f2(){cout << "D::virtual"<<endl;}
};
void main()
{
B* b = new B;
D* d = new D;
B* bd = new D;
D* db1 = (D*)bd;
D* db2 = (D*)b;
b->f1();
b->f2();
d->f1();
d->f2();
bd->f1();
bd->f2();
db1->f1();
db1->f2();
db2->f1();
db2->f2();
}
Выдает
B::simple
B::virtual
D::simple 12345
D::virtual
B::simple
B::virtual
D::simple 12345
D::virtual
D::simple 12345
D::virtual
---
|
| | | | | | | | | | |
Давай разбираться... 09.01.02 17:19
Автор: :-) <:-)> Статус: Elderman Отредактировано 09.01.02 17:21 Количество правок: 1
|
> Мне ж не жалко было и проверить :) По-прежнему работает, > зараза, хотя и не должно :))
Как раз так должно. Но я же сказал, что надо сделать невиртуальной B::f2(), а ты сделал невиртуальными все ф-ии. Поэтому и работает. D::f2() нужно было оставить виртуальной. Ты не думай, перед тем, как написать, я тоже проверил - падает :-))
|
| | | | | | | | | | | |
Давай разбираться... 09.01.02 17:32
Автор: dl <Dmitry Leonov>
|
> > Мне ж не жалко было и проверить :) По-прежнему > работает, > > зараза, хотя и не должно :)) > > Как раз так должно. Но я же сказал, что > надо сделать невиртуальной B::f2(), а ты > сделал невиртуальными все ф-ии. Поэтому и работает. > D::f2() нужно было оставить виртуальной. > Ты не думай, перед тем, как написать, я тоже проверил - > падает :-))
А, ну да. действительно. Но так совсем жестоко, превратить простую функцию в виртуальную, у меня на такое просто рука не поднялась :))
|
|
|