информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Атака на InternetСетевые кракеры и правда о деле ЛевинаГде водятся OGRы
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
 Умер Никлаус Вирт 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
если вы видите этот текст, отключите в настройках форума использование JavaScript
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
Давай разбираться... 09.01.02 13:12  Число просмотров: 1627
Автор: 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 используется какое-нибудь поле из производного класса - по крайней мере, в простых примерах.
<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: 1 s   Design: Vadim Derkach