информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Spanning Tree Protocol: недокументированное применениеСтрашный баг в WindowsПортрет посетителя
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Бэкдор в xz/liblzma, предназначенный... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
если вы видите этот текст, отключите в настройках форума использование JavaScript
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
Давай разбираться... 09.01.02 17:02  Число просмотров: 1686
Автор: 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


---
<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() нужно было оставить виртуальной.
> Ты не думай, перед тем, как написать, я тоже проверил -
> падает :-))

А, ну да. действительно. Но так совсем жестоко, превратить простую функцию в виртуальную, у меня на такое просто рука не поднялась :))
1




Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach