Здравствуйте, <Аноним>, Вы писали:
А>Я ж говорю — vtbl можно найти только если есть объект, т.к. его тело содержит ссылку на нее. Если нужно невиртуально вызвать метод, объявленный виртуальным, то в вызове нужно явно указать имя класса:
Именно! Огромное Вам спасибо! Это то, что мне нужно было!
template<class class_name, class objectT>
void AssertKindOf(objectT* a)
{
ASSERT(a->IsKindOf( ((class_name*)NULL)->class_name::GetRuntimeClass() ));
}
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, <Аноним>, Вы писали:
А>>Виртуальные метод так вызвать нельзя, потому что виртуальных методов "не использующих this" не существует. this используется реализацией вызова виртуального метода, так как необходимо обратиться к виртуальной таблице, ссылка на которую находится в теле объекта и на нее указывает this. Через (class_name*)NULL можно вызвать невиртуальный метод, не использующий this. Тогда, правда, такой метод следовало бы сделать статическим, раз ему this не нужен.
C>Ну почему же нет? Машинный код виртуального метода ничем не отличается от обычной функции. Нужно только определить, где в памяти находится этот метод и вызвать его, в параметре EBX указав 0 (this — не используется).
C>Я знаю, что это возможно, но не знаю как. Догадываюсь, что нужно что-то мутить с vtbl.
Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге)
Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта.
Решения в рамках Стандарта не существует.
Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
Здравствуйте, jazzer, Вы писали:
J>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге) J>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта. J>Решения в рамках Стандарта не существует. J>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
Ок.
А это
Здравствуйте, Chez, Вы писали:
C>Именно! Огромное Вам спасибо! Это то, что мне нужно было!
Вот так и приходится хакать, вместо того, чтобы в MFC с самого начала делали бы пару — статический метод и виртуальный. И не пришлось бы им тогда выделываться с макросом...
Перекуём баги на фичи!
Re[6]: Вызов виртуального метода для класса
От:
Аноним
Дата:
19.10.04 09:36
Оценка:
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Chez, Вы писали:
C>>Именно! Огромное Вам спасибо! Это то, что мне нужно было!
К>Вот так и приходится хакать, вместо того, чтобы в MFC с самого начала делали бы пару — статический метод и виртуальный. И не пришлось бы им тогда выделываться с макросом...
Ну не проинтуичили в микрософте сразу. Когда писался CRuntimeClass мелкомягкий компилятор не токмо что шаблоны, но и исключения не поддерживал
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, jazzer, Вы писали:
J>>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге) J>>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта. J>>Решения в рамках Стандарта не существует. J>>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm. C>Ок. 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>т.е. разыменование нулевого указателя — неопределенное поведение
Хм... А что собственно вы понимаете под "разыменовыванием"? Операторы * и -> ?
Если так, то ну и что? Чтения из нулевого адреса не происходит, записи тоже. В данном случае лишь 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>>А это
— решение в рамках стандарта?
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)
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Кодт, Вы писали:
К>>Здравствуйте, 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 неэквивалентны.
Здравствуйте, Аноним, Вы писали:
А>Да, 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.
Здравствуйте, 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>Но ума не приложу, где такое может произойти на практике. :???:
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, то пофиг интерпретаторы.
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 вообще не имеет смысл заморачиваться
Здравствуйте, Аноним, Вы писали:
А>>>Работая с 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 — ИДА в сигнатурах запуталась). См. комменты после ; :
Разница лишь в том как они передают указатели в основном, и насколько они наворочены в оптимизации. Но адресации вирт. методов в асме всегда косвенная регистровая. Где содержимое регистра есть разыменованный this, т.е. как раз vtbl.
Так что есть стандарты, а есть реальные компилеры, которые в этом случае все как один себя и ведут
Здравствуйте, erithion aka tyomik, Вы писали:
EAT>Так что есть стандарты, а есть реальные компилеры, которые в этом случае все как один себя и ведут
Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно. Но такие случаи не часты.
Здравствуйте, Вы писали:
А>На большинстве стандартных вычислительных архитекрур 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;
}
Тока естесственно указатель на себя будет нулевой, и это равносильно вызову стат. метода...