Re[4]: Вызов виртуального метода для класса
От: Chez Россия  
Дата: 19.10.04 09:21
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Я ж говорю — vtbl можно найти только если есть объект, т.к. его тело содержит ссылку на нее. Если нужно невиртуально вызвать метод, объявленный виртуальным, то в вызове нужно явно указать имя класса:

Именно! Огромное Вам спасибо! Это то, что мне нужно было!

template<class class_name, class objectT>
void AssertKindOf(objectT* a)
{
    ASSERT(a->IsKindOf( ((class_name*)NULL)->class_name::GetRuntimeClass() ));
}
— работает!
Chez, ICQ# 161095094
Re[3]: Вызов виртуального метода для класса
От: jazzer Россия Skype: enerjazzer
Дата: 19.10.04 09:27
Оценка:
Здравствуйте, Chez, Вы писали:

C>Здравствуйте, <Аноним>, Вы писали:


А>>Виртуальные метод так вызвать нельзя, потому что виртуальных методов "не использующих this" не существует. this используется реализацией вызова виртуального метода, так как необходимо обратиться к виртуальной таблице, ссылка на которую находится в теле объекта и на нее указывает this. Через (class_name*)NULL можно вызвать невиртуальный метод, не использующий this. Тогда, правда, такой метод следовало бы сделать статическим, раз ему this не нужен.


C>Ну почему же нет? Машинный код виртуального метода ничем не отличается от обычной функции. Нужно только определить, где в памяти находится этот метод и вызвать его, в параметре EBX указав 0 (this — не используется).


C>Я знаю, что это возможно, но не знаю как. Догадываюсь, что нужно что-то мутить с vtbl.


Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге)
Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта.
Решения в рамках Стандарта не существует.
Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Вызов виртуального метода для класса
От: Chez Россия  
Дата: 19.10.04 09:31
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге)

J>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта.
J>Решения в рамках Стандарта не существует.
J>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
Ок.
А это
Автор:
Дата: 19.10.04
— решение в рамках стандарта?
Chez, ICQ# 161095094
Re[5]: Вызов виртуального метода для класса
От: Кодт Россия  
Дата: 19.10.04 09:32
Оценка: +1
Здравствуйте, Chez, Вы писали:

C>Именно! Огромное Вам спасибо! Это то, что мне нужно было!


Вот так и приходится хакать, вместо того, чтобы в MFC с самого начала делали бы пару — статический метод и виртуальный. И не пришлось бы им тогда выделываться с макросом...
Перекуём баги на фичи!
Re[6]: Вызов виртуального метода для класса
От: Аноним  
Дата: 19.10.04 09:36
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Chez, Вы писали:


C>>Именно! Огромное Вам спасибо! Это то, что мне нужно было!


К>Вот так и приходится хакать, вместо того, чтобы в MFC с самого начала делали бы пару — статический метод и виртуальный. И не пришлось бы им тогда выделываться с макросом...


Ну не проинтуичили в микрософте сразу. Когда писался CRuntimeClass мелкомягкий компилятор не токмо что шаблоны, но и исключения не поддерживал
Re[5]: Вызов виртуального метода для класса
От: jazzer Россия Skype: enerjazzer
Дата: 19.10.04 09:54
Оценка:
Здравствуйте, Chez, Вы писали:

C>Здравствуйте, jazzer, Вы писали:


J>>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге)

J>>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта.
J>>Решения в рамках Стандарта не существует.
J>>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
C>Ок.
C>А это
Автор:
Дата: 19.10.04
— решение в рамках стандарта?


конечно же нет.
5.2.5/3
If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

соответственно запись
((C2*)0)->Met();

означает
(*(C2*)0).Met();

т.е. разыменование нулевого указателя — неопределенное поведение
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: Вызов виртуального метода для класса
От: Chez Россия  
Дата: 19.10.04 10:05
Оценка:
Здравствуйте, jazzer, Вы писали:

J>конечно же нет.

J>5.2.5/3
J>If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

J>соответственно запись
((C2*)0)->Met();

J>означает
J>(*(C2*)0).Met();
J>

J>т.е. разыменование нулевого указателя — неопределенное поведение
Хм... А что собственно вы понимаете под "разыменовыванием"? Операторы * и -> ?
Если так, то ну и что? Чтения из нулевого адреса не происходит, записи тоже. В данном случае лишь this-у присваивается NULL.

Я конечно понимаю, что теоретически реализация может быть иной, и запись\чтение таки произойдёт и приведёт к краху.
Но ума не приложу, где такое может произойти на практике.
Chez, ICQ# 161095094
Re[6]: Вызов виртуального метода для класса
От: Аноним  
Дата: 19.10.04 10:09
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, Chez, Вы писали:


C>>Здравствуйте, jazzer, Вы писали:


J>>>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге)

J>>>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта.
J>>>Решения в рамках Стандарта не существует.
J>>>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
C>>Ок.
C>>А это
Автор:
Дата: 19.10.04
— решение в рамках стандарта?


J>конечно же нет.

J>5.2.5/3
J>If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

J>соответственно запись
((C2*)0)->Met();

J>означает
J>(*(C2*)0).Met();
J>

J>т.е. разыменование нулевого указателя — неопределенное поведение

Работая с MFC нет смысла особо заморачиваться насчет стандарта. Как говаривал старик Гете: "Суха теория, мой друг, а древо жизни пышно зеленеет". Вряд ли компилятор при обработке вызова E1->E2() сначала разыменовывает Е1. Обращение по указателю E1 нужно только при виртуальной вызове. Иначе этот указатель просто (без какого-либо анализа) передается методу в качестве скрытого параметра this.
На большинстве стандартных вычислительных архитекрур imho это будет работать. Хотя в архитектурах типа .NET. возможно и нет (сорри за каламбур), но там и MFC нет (снова сорри) — там есть свои средства для доступа к информации о типе во время исполнения (более мощные чем CRuntimeClass в MFC)
Re[7]: Вызов виртуального метода для класса
От: GlebZ Россия  
Дата: 19.10.04 10:20
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Кодт, Вы писали:


К>>Здравствуйте, Chez, Вы писали:


C>>>Именно! Огромное Вам спасибо! Это то, что мне нужно было!


К>>Вот так и приходится хакать, вместо того, чтобы в MFC с самого начала делали бы пару — статический метод и виртуальный. И не пришлось бы им тогда выделываться с макросом...


А>Ну не проинтуичили в микрософте сразу. Когда писался CRuntimeClass мелкомягкий компилятор не токмо что шаблоны, но и исключения не поддерживал


Да насколько я помню, при MFC 1.0 компилятор вообще не знал, что такое RTTI. После появления последнего, я стараюсь не пользоваться CRuntimeClass. Не вижу смысла разделять MFC классы и не MFC.

С уважением, Gleb
Re[8]: Вызов виртуального метода для класса
От: Аноним  
Дата: 19.10.04 10:32
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Здравствуйте, Аноним, Вы писали:


А>>Здравствуйте, Кодт, Вы писали:


К>>>Здравствуйте, Chez, Вы писали:


C>>>>Именно! Огромное Вам спасибо! Это то, что мне нужно было!


К>>>Вот так и приходится хакать, вместо того, чтобы в MFC с самого начала делали бы пару — статический метод и виртуальный. И не пришлось бы им тогда выделываться с макросом...


А>>Ну не проинтуичили в микрософте сразу. Когда писался CRuntimeClass мелкомягкий компилятор не токмо что шаблоны, но и исключения не поддерживал


GZ>Да насколько я помню, при MFC 1.0 компилятор вообще не знал, что такое RTTI. После появления последнего, я стараюсь не пользоваться CRuntimeClass. Не вижу смысла разделять MFC классы и не MFC.


GZ>С уважением, Gleb


Да, RTTI вообще поддерживается этим компилятором совсем недавно. Следует, однако, иметь в виду, что в CRuntimeClass есть фичи, которые не поддерживаются RTTI, а именно CreateObject, IsDerivedFrom и поддержка CArchive. Нельзя сказать, что без всего этого нельзя обойтись, но очевидно, что RTTI и CRuntimeClass неэквивалентны.
Re[9]: Вызов виртуального метода для класса
От: GlebZ Россия  
Дата: 19.10.04 11:29
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Да, RTTI вообще поддерживается этим компилятором совсем недавно. Следует, однако, иметь в виду, что в CRuntimeClass есть фичи, которые не поддерживаются RTTI, а именно CreateObject, IsDerivedFrom и поддержка CArchive. Нельзя сказать, что без всего этого нельзя обойтись, но очевидно, что RTTI и CRuntimeClass неэквивалентны.


Несколько самозабвенно написал, каюсь, однако почти всегда обхожусь: CreateObject — обычно создается другими способами, IsDerivedFrom — xxx_cast() более быстрые процедуры. Что касается CArchive, то почему-то в последнее время не приходится пользоваться. Проще получается переопределять процедуру загрузки — сохранения из-за сложностей форматов. Это можно сказать является просто особенности тех проектов с применение MFC, которые приходилось делать в последнее время. К тому-же приходится сидеть на нескольких платформах, и отвыкать от MFC заморочек.

С уважением, Gleb.
Re[10]: Вызов виртуального метода для класса
От: Аноним  
Дата: 19.10.04 11:32
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Здравствуйте, Аноним, Вы писали:


А>>Да, RTTI вообще поддерживается этим компилятором совсем недавно. Следует, однако, иметь в виду, что в CRuntimeClass есть фичи, которые не поддерживаются RTTI, а именно CreateObject, IsDerivedFrom и поддержка CArchive. Нельзя сказать, что без всего этого нельзя обойтись, но очевидно, что RTTI и CRuntimeClass неэквивалентны.


GZ>Несколько самозабвенно написал, каюсь, однако почти всегда обхожусь: CreateObject — обычно создается другими способами, IsDerivedFrom — xxx_cast() более быстрые процедуры. Что касается CArchive, то почему-то в последнее время не приходится пользоваться. Проще получается переопределять процедуру загрузки — сохранения из-за сложностей форматов. Это можно сказать является просто особенности тех проектов с применение MFC, которые приходилось делать в последнее время. К тому-же приходится сидеть на нескольких платформах, и отвыкать от MFC заморочек.


GZ>С уважением, Gleb.


Да в общем-то я с Вами полностью согласен.
Re[7]: Вызов виртуального метода для класса
От: jazzer Россия Skype: enerjazzer
Дата: 19.10.04 13:01
Оценка:
Здравствуйте, Chez, Вы писали:

C>Здравствуйте, jazzer, Вы писали:


J>>конечно же нет.

J>>5.2.5/3
J>>If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

J>>соответственно запись
((C2*)0)->Met();

J>>означает
J>>(*(C2*)0).Met();
J>>

J>>т.е. разыменование нулевого указателя — неопределенное поведение
C>Хм... А что собственно вы понимаете под "разыменовыванием"? Операторы * и -> ?
Какая разница, что понимаю я?
Важно, как это определяет Стандарт. А он это понимает как выражение приведения, перед которым стоит символ *.
C>Если так, то ну и что? Чтения из нулевого адреса не происходит, записи тоже. В данном случае лишь this-у присваивается NULL.

Происходит разыменование.
Этого достаточно для неопределенного поведения.
Хотя есть предложения по изменению стандарта в этой части.

C>Я конечно понимаю, что теоретически реализация может быть иной, и запись\чтение таки произойдёт и приведёт к краху.

C>Но ума не приложу, где такое может произойти на практике. :???:

в cint, думаю, это вполне возможно
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[8]: Вызов виртуального метода для класса
От: Lorenzo_LAMAS  
Дата: 19.10.04 13:05
Оценка:
J>в cint, думаю, это вполне возможно

Да, он это не пропустит.
Of course, the code must be complete enough to compile and link.
Re[9]: Вызов виртуального метода для класса
От: Аноним  
Дата: 19.10.04 13:09
Оценка:
Здравствуйте, Lorenzo_LAMAS, Вы писали:

J>>в cint, думаю, это вполне возможно


L_L>Да, он это не пропустит.


Во-во, во всяких интерпретаторах, типа cint и .NET не будет работать. А на большинстве компиляторов в машинный код — будет. Опять же, если имеем дело с MFC, то пофиг интерпретаторы.
Re[7]: Вызов виртуального метода для класса
От: jazzer Россия Skype: enerjazzer
Дата: 19.10.04 13:37
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Работая с MFC нет смысла особо заморачиваться насчет стандарта.


Бери шире: на VC6.0 вообще не имеет смысл заморачиваться

Удалено избыточное цитирование. -- ПК
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[8]: Вызов виртуального метода для класса
От: Аноним  
Дата: 19.10.04 13:38
Оценка:
Здравствуйте, jazzer, Вы писали:

А>>Работая с MFC нет смысла особо заморачиваться насчет стандарта.


J>Бери шире: на VC6.0 вообще не имеет смысл заморачиваться


Натюрлих

Удалено избыточное цитирование. -- ПК
Re[9]: Вызов виртуального метода для класса
От: erithion aka tyomik  
Дата: 19.10.04 17:06
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

А>>>Работая с MFC нет смысла особо заморачиваться насчет стандарта.


J>>Бери шире: на VC6.0 вообще не имеет смысл заморачиваться


А>Натюрлих


Не знаю как там со стандартом, но большинство известных мне С++ современных компиляторов генерят в этом отношении приблизительно одинаковый код. Вот тест:

class test
{
public:
    void test_fn();
    virtual void virt_fn();
};
void test::test_fn()
{
    return;
}
void test::virt_fn()
{
    return;
}

int _tmain(int argc, _TCHAR* argv[])
{
    test* t = 0;
    (*t).test_fn();
    t->virt_fn();        // Будет креш
    return 0;
}

Вот его асм(сорри за stdInit — ИДА в сигнатурах запуталась). См. комменты после ; :

.text:00401020 _main           proc near               ; CODE XREF: start+16Ep
.text:00401020
.text:00401020 t               = dword ptr -4
.text:00401020
.text:00401020                 push    ebp
.text:00401021                 mov     ebp, esp
.text:00401023                 push    ecx
.text:00401024                 mov     [ebp+t], 0                   ; test* t = 0;
.text:0040102B                 mov     ecx, [ebp+t]
.text:0040102E                 call    ??1_Init_locks@std@@QAE@XZ   ; t->test_fn();
.text:00401033                 mov     eax, [ebp+t]
.text:00401036                 mov     edx, [eax]                   ; edx = this->vftbl; // (*this)
.text:00401038                 mov     ecx, [ebp+t]
.text:0040103B                 call    dword ptr [edx]              ; vftbl[0]() - crash;
.text:0040103D                 xor     eax, eax
.text:0040103F                 mov     esp, ebp
.text:00401041                 pop     ebp
.text:00401042                 retn
.text:00401042 _main           endp


Разница лишь в том как они передают указатели в основном, и насколько они наворочены в оптимизации. Но адресации вирт. методов в асме всегда косвенная регистровая. Где содержимое регистра есть разыменованный this, т.е. как раз vtbl.
Так что есть стандарты, а есть реальные компилеры, которые в этом случае все как один себя и ведут

Удалено избыточное цитирование. -- ПК
... locked in silent monolog ...
Re[10]: Вызов виртуального метода для класса
От: erithion aka tyomik  
Дата: 19.10.04 17:10
Оценка:
Здравствуйте, erithion aka tyomik, Вы писали:

EAT>Так что есть стандарты, а есть реальные компилеры, которые в этом случае все как один себя и ведут


Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно. Но такие случаи не часты.

Удалено избыточное цитирование. -- ПК
... locked in silent monolog ...
Re[7]: Вызов виртуального метода для класса
От: erithion aka tyomik  
Дата: 19.10.04 17:21
Оценка:
Здравствуйте, Вы писали:

А>На большинстве стандартных вычислительных архитекрур imho это будет работать. Хотя в архитектурах типа .NET. возможно и нет (сорри за каламбур), но там и MFC нет (снова сорри) — там есть свои средства для доступа к информации о типе во время исполнения (более мощные чем CRuntimeClass в MFC)


Но сработае такой вызов:

class test
{
public:
    virtual void virt_fn();
};
void test::virt_fn()
{
    return;
}

int _tmain(int argc, _TCHAR* argv[])
{
    test* t = 0;
    t->test::virt_fn();
    return 0;
}

Тока естесственно указатель на себя будет нулевой, и это равносильно вызову стат. метода...

Удалено избыточное цитирование. -- ПК
... locked in silent monolog ...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.