Есть класс наследованный от CObject. Нужно вызвать виртуальный метод этого класса, не имея экземпляра класса (этот метод не использует this). Как это сделать?
Понимаю, что нужно каким-то образом получить vtbl этого класса и найти в нём смещение нужного метода... Но вот как?
А нужно что-то вроде:
class CAssert
{
public:
template<class class_name, class objectT>
void KindOf(objectT* pObject)
{ ASSERT(pObject->IsKindOf( ((class_name*)NULL)->GetRuntimeClass() )); }
// только это не работает
};
Естественно это не работает. Разыменовывать нулевые указатели запрещено.
Лучше всего — отказаться от убогой информации о типе времени выполнения на методах из кайнд оф и перейти к стандартному typeid.
Здравствуйте, Vamp, Вы писали:
V>Естественно это не работает. Разыменовывать нулевые указатели запрещено. V>Лучше всего — отказаться от убогой информации о типе времени выполнения на методах из кайнд оф и перейти к стандартному typeid.
Не, RTTI не катит.
Здравствуйте, korzhik, Вы писали:
K>про виртуальную функцию не понял, а чтобы вышеприведённый кусок кода заработал, пиши так:
Вызвать вирутальную функцию с this = абы_шо, т.е. недействительный экземпляр. K>
Здравствуйте, Chez, Вы писали:
C>В шаблоне это работать не будет. C>Макрос RUNTIME_CLASS(class_name) разворачиватся в ...class##class_name... В результате ошибка C>
C>error C2039: 'classclass_name' : is not a member of 'CWnd'
да, действительно
что то сразу не сообразил, что у вас там шаблон
ну что ж будем думать...
Здравствуйте, Chez, Вы писали:
C>Есть класс наследованный от CObject. Нужно вызвать виртуальный метод этого класса, не имея экземпляра класса (этот метод не использует this). Как это сделать?
C>Понимаю, что нужно каким-то образом получить vtbl этого класса и найти в нём смещение нужного метода... Но вот как?
C>А нужно что-то вроде: C>
C>class CAssert
C>{
C>public:
C> template<class class_name, class objectT>
C> void KindOf(objectT* pObject)
C> { ASSERT(pObject->IsKindOf( ((class_name*)NULL)->GetRuntimeClass() )); }
C> // только это не работает
C>};
C>
C>Помогите плз
что за глупости? какой виртуальный метод если объекта нет? Насколько я понял тебе нужен просто static метод.
JS>что за глупости? какой виртуальный метод если объекта нет? Насколько я понял тебе нужен просто static метод.
Это не static метод, в том то всё и дело, а virtual. Но я хотел бы его вызвать, будто бы он static.
Замечу ещё раз, что код этого метода не используетthis.
Здравствуйте, Chez, Вы писали:
C>Есть класс наследованный от CObject. Нужно вызвать виртуальный метод этого класса, не имея экземпляра класса (этот метод не использует this). Как это сделать?
C>Понимаю, что нужно каким-то образом получить vtbl этого класса и найти в нём смещение нужного метода... Но вот как?
C>А нужно что-то вроде: C>
C>class CAssert
C>{
C>public:
C> template<class class_name, class objectT>
C> void KindOf(objectT* pObject)
C> { ASSERT(pObject->IsKindOf( ((class_name*)NULL)->GetRuntimeClass() )); }
C> // только это не работает
C>};
C>
C>Помогите плз
Виртуальные метод так вызвать нельзя, потому что виртуальных методов "не использующих this" не существует. this используется реализацией вызова виртуального метода, так как необходимо обратиться к виртуальной таблице, ссылка на которую находится в теле объекта и на нее указывает this. Через (class_name*)NULL можно вызвать невиртуальный метод, не использующий this. Тогда, правда, такой метод следовало бы сделать статическим, раз ему this не нужен.
Chez wrote:
> JS>что за глупости? какой виртуальный метод если объекта нет? Насколько я понял тебе нужен просто static метод. > Это не static метод, в том то всё и дело, а virtual. Но я хотел бы его вызвать, будто бы он static. > Замечу ещё раз, что код этого метода не используетthis.
А что это за метод (вообще, в c++ не существует методов, только ф-ции-члены ), кто его создатель — ты или творцы MFC?
Это МФСишное убожество времент 4 версии компилятора визуал си.
А что значит "в c++ не существует методов, только ф-ции-члены"? Функции-члены в С++ и называются методами. Или сообщениями даже
Здравствуйте, MaximE, Вы писали:
ME>Chez wrote:
>> JS>что за глупости? какой виртуальный метод если объекта нет? Насколько я понял тебе нужен просто static метод. >> Это не static метод, в том то всё и дело, а virtual. Но я хотел бы его вызвать, будто бы он static. >> Замечу ещё раз, что код этого метода не используетthis.
ME>А что это за метод (вообще, в c++ не существует методов, только ф-ции-члены ), кто его создатель — ты или творцы MFC?
Ну ты просто гений. в c++ не существует ни методов ни функций членов, поскольку стандарт на английском писан. так что же теперь по русски не разговаривать?
ME>-- ME>Maxim Yegorushkin
Здравствуйте, Chez, Вы писали:
JS>>что за глупости? какой виртуальный метод если объекта нет? Насколько я понял тебе нужен просто static метод. C>Это не static метод, в том то всё и дело, а virtual. Но я хотел бы его вызвать, будто бы он static. C>Замечу ещё раз, что код этого метода не используетthis.
Уже сама по себе виртуальность использует this.
А тебе, видимо, нужно другое: виртуальный метод адресации к статическому
typedef int (*Fun)(int);
class Foo
{
public:
virtual Fun have_fun() const { return my_fun; }
private:
static int my_fun(int x) { return x*2; }
};
class Bar : public Foo
{
public:
Fun have_fun() const { return my_own_fun; }
private:
static int my_own_fun(int x) { return x*x; }
};
void test_fun(Fun fun)
{
cout << fun(10) << endl;
}
void test_obj(Foo* p)
{
test_fun(p->have_fun());
}
main()
{
Foo *a = new Foo(), *b = new Bar();
test_obj(a);
test_obj(b);
delete a; delete b;
}
ME>А что это за метод (вообще, в c++ не существует методов, только ф-ции-члены ), кто его создатель — ты или творцы MFC?
ME>-- ME>Maxim Yegorushkin
Творцы MFC. CObject::GetRuntimeClass
Здравствуйте, <Аноним>, Вы писали:
А>Виртуальные метод так вызвать нельзя, потому что виртуальных методов "не использующих this" не существует. this используется реализацией вызова виртуального метода, так как необходимо обратиться к виртуальной таблице, ссылка на которую находится в теле объекта и на нее указывает this. Через (class_name*)NULL можно вызвать невиртуальный метод, не использующий this. Тогда, правда, такой метод следовало бы сделать статическим, раз ему this не нужен.
Ну почему же нет? Машинный код виртуального метода ничем не отличается от обычной функции. Нужно только определить, где в памяти находится этот метод и вызвать его, в параметре EBX указав 0 (this — не используется).
Я знаю, что это возможно, но не знаю как. Догадываюсь, что нужно что-то мутить с vtbl.
Здравствуйте, Кодт, Вы писали:
К>Уже сама по себе виртуальность использует this.
Вот я и думаю если туда подсунуть какой-нить суррогатный this... К>А тебе, видимо, нужно другое: виртуальный метод адресации к статическому
Я бы это обязательно добавил, если бы проектировал MFC.
GetRuntimeClass() — виртуальный, и статического аналога нет
Chez, ICQ# 161095094
Re[3]: аноним 2
От:
Аноним
Дата:
19.10.04 09:07
Оценка:
C>Ну почему же нет? Машинный код виртуального метода ничем не отличается от обычной функции. Нужно только определить, где в памяти находится этот метод и вызвать его, в параметре EBX указав 0 (this — не используется).
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, <Аноним>, Вы писали:
А>>Виртуальные метод так вызвать нельзя, потому что виртуальных методов "не использующих this" не существует. this используется реализацией вызова виртуального метода, так как необходимо обратиться к виртуальной таблице, ссылка на которую находится в теле объекта и на нее указывает this. Через (class_name*)NULL можно вызвать невиртуальный метод, не использующий this. Тогда, правда, такой метод следовало бы сделать статическим, раз ему this не нужен.
C>Ну почему же нет? Машинный код виртуального метода ничем не отличается от обычной функции. Нужно только определить, где в памяти находится этот метод и вызвать его, в параметре EBX указав 0 (this — не используется).
C>Я знаю, что это возможно, но не знаю как. Догадываюсь, что нужно что-то мутить с vtbl.
Я ж говорю — vtbl можно найти только если есть объект, т.к. его тело содержит ссылку на нее. Если нужно невиртуально вызвать метод, объявленный виртуальным, то в вызове нужно явно указать имя класса:
struct C1
{
virtual int Met() { return 1; }
};
struct C2 : public C1
{
virtual int Met() { return 2; }
};
int _tmain(int argc, _TCHAR* argv[])
{
// эти два вызова работают: невиртуальный вызов виртуальной функции
int i2=((C2*)0)->C2::Met();
int i1=((C2*)0)->C1::Met();
assert( i2==2);
assert( i1==1);
int ifail=((C2*)0)->Met(); // здесь падает: виртуальный вызов через null-указатель
return 0;
Здравствуйте, <Аноним>, Вы писали:
А>Я ж говорю — 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;
}
Тока естесственно указатель на себя будет нулевой, и это равносильно вызову стат. метода...
Здравствуйте, erithion aka tyomik, Вы писали:
EAT>Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно. Но такие случаи не часты.
А можно пример, когда такое возможно?
Chez, ICQ# 161095094
Re[4]: Вызов виртуального метода для класса
От:
Аноним
Дата:
20.10.04 07:53
Оценка:
Здравствуйте, jazzer, Вы писали:
J>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге) J>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта. J>Решения в рамках Стандарта не существует. J>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, erithion aka tyomik, Вы писали:
EAT>>Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно. Но такие случаи не часты. C>А можно пример, когда такое возможно?
Пожалуйста:
class a
{
public:
a()
{
printf("a::constructor");
}
virtual void fn()
{
printf("a::fn");
}
};
class c: public virtual a
{
public:
c()
{
printf("c::constructor");
}
void fn()
{
printf("c::fn");
}
};
int _tmain(int argc, _TCHAR* argv[])
{
c* i = new c();
i->fn();
delete i;
return 0;
}
Вот более или менее понятный разбор того, что происходит в бин. коде:
.rdata:0040811C c_vtbl dd offset c_fn ; DATA XREF: c_constructor+2Co
.rdata:00408120 init dd 0 ; DATA XREF: c_constructor+10o
.rdata:00408124 dd 8
.rdata:00408138 a_vtbl dd offset a_fn ; DATA XREF: a_constructor+Ao
Вкратце, происходит следующее:
Внутри конструктора класса с видим, что он в самое начало вирт. таблицы записывает некую таблицу индексов, которой потом пользуется для инициализации и позднее поиска виртуальных таблиц и ф-й в них. Мои комментарии справа подразумевают, что в таблице смещения не в байтах, а в DWORD'ах, т.е. индексы.
Далее происходит вызов конструктора класса а, который записывает во 2-й элемент таблицы адрес вирт. таблицы класса а. После этого происходит замещение этого адреса адресом вирт. таблицы класса с, которая вмещает в себя невиртуальную ф-ю в классе с. После выполняется все остальное тело конструктора.
В main можно увидеть, как вызов i->fn(); происходит косвенно через регистр еах.
P.S.: Спасибо за вопрос. Это заинтересовало меня, поскольку в VS 2003 .NET сишный компилер генерит несколько иной код. В частности меня заинтересовал единственный параметр в конструкторе класса с равный 1. В конструкторе же можно видеть, что ежели этот параметр равен нулю, то вызова конструктора базового класса не произойдет. Пока я не знаю к какой опере это относится и как можно добится того, чтоб конструктор предка не вызывался вовсе. Кроме того, при обычном наследовании(невиртуальном) у конструктора отсутствует этот параметр вообще. Так что это повод провести более полный анализ.
Thx!!!
Здравствуйте, erithion aka tyomik, Вы писали:
EAT>>>Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно.
c::fn — виртуальная функция.
EAT>P.S.: Спасибо за вопрос. Это заинтересовало меня, поскольку в VS 2003 .NET сишный компилер генерит несколько иной код. В частности меня заинтересовал единственный параметр в конструкторе класса с равный 1. В конструкторе же можно видеть, что ежели этот параметр равен нулю, то вызова конструктора базового класса не произойдет. Пока я не знаю к какой опере это относится и как можно добится того, чтоб конструктор предка не вызывался вовсе. Кроме того, при обычном наследовании(невиртуальном) у конструктора отсутствует этот параметр вообще. Так что это повод провести более полный анализ.
Виртуальные базовые классы инициализируются только из конструктора most derived class.
Здравствуйте, elcste, Вы писали:
E>Здравствуйте, erithion aka tyomik, Вы писали: E>c::fn — виртуальная функция.
Еще раз повторюсь, меня не интересуют стандарты, несмотря на то, что я тоже читал Страуструпа и другую не менее интересную литературу и тоже прекрасно помню, что всеми ими такой метод класса обзывается виртуальным.
С точки же зрения исследователя, я знаю другое, а именно, что это чистый метод класса в реализации МС С++ компилятора. Его виртуальность возникает вследствие перекрытия имен, которая разрешается в пользу виртуальности метода наследника. И виртуальность его косвенная, поскольку реализуется через переходник. На что собсно код и указывает. В виртуальной таблице класса с не адрес самой процедуры, а адрес так называемого переходника на процедуру(метод класса c::fn) с коррекцией указателя перед переходом. Обрати внимание еще раз, мож теперь понятнее буит:
Отсюда видно, что это есть механизм, с помощью которого возможна реализация положений стандарта и возникает он только при виртуальном наследовании. На самом же деле это есть обычный метод класса.
E>Виртуальные базовые классы инициализируются только из конструктора most derived class.
Я сказал про параметр, вследствие равенства нулю которого вызова конструктора базового класса вообще не происходит. Как бы я не наследовался, вызов все равно будет, т.е. если присутствует один параметр в конструктор, то пока что у меня он всегда был равен 1. Равенства же его 0 я пока не смог добиться. И пока не упомню ситуаций, когда такое возможно. Разве что для нужд компилятора в специфических ситуациях, когда тока так можно выйти из положения.
E>P.S. Учите язык.
Спасибо! Торжественно обещаю
А какой? Алгол?
Я слышал множество пунктов стандарта и предложений по реализации взято именно оттуда.
Здравствуйте, erithion aka tyomik, Вы писали:
E>>c::fn — виртуальная функция.
EAT>Еще раз повторюсь, меня не интересуют стандарты, несмотря на то, что я тоже читал Страуструпа и другую не менее интересную литературу и тоже прекрасно помню, что всеми ими такой метод класса обзывается виртуальным.
То есть эту функцию невиртуальной называете только Вы. Или еще кто-то?
EAT>С точки же зрения исследователя, я знаю другое, а именно, что это чистый метод класса в реализации МС С++ компилятора.
Вот только меня, в свою очередь, совершенно не интересует "реализация МС С++ компилятора". В данный момент я работаю с процессором Blackfin (ADSP-BF561), и мне интересен код, который генерирует VisualDSP++ от Analog Devices. Не хотите исследовать его для разнообразия?
Обратите внимание на то, что вызов виртуальной функции c::fn происходит косвенно, через таблицу виртуальных функций, несмотря на совпадение в данном случае статического и динамического типов указуемого i объекта.
EAT>Его виртуальность возникает вследствие перекрытия имен, которая разрешается в пользу виртуальности метода наследника.
Bravo! You made my day!
E>>Виртуальные базовые классы инициализируются только из конструктора most derived class. EAT>Я сказал про параметр, вследствие равенства нулю которого вызова конструктора базового класса вообще не происходит.
Прочитав учебник, Вы осознаете смысл сказанного выше. Если же Вы принципиально не читаете ничего, кроме ассемблерных листингов, помедитируйте над результатом трансляции следующего примера.
Здравствуйте, elcste, Вы писали:
E>То есть эту функцию невиртуальной называете только Вы. Или еще кто-то?
Я б сказал, что для меня(и не только) она таковой не является именно потому, что стандартный подход реверсирования в этом случае несколько изменяется. Именно изза наличия переходника.
E>Вот только меня, в свою очередь, совершенно не интересует "реализация МС С++ компилятора". В данный момент я работаю с процессором Blackfin (ADSP-BF561), и мне интересен код, который генерирует VisualDSP++ от Analog Devices. Не хотите исследовать его для разнообразия?
Нет, спасибо, пока не хочу.Поскольку меня ждет не менее интересная работа с кодом ARM-процессора и тонкостями его построения компилятором.
Но раз уж речь зашла об этом, то компилер С++ для ARM'а вообще не признает виртуальное наследование. А вызов виртуальных методов производит непосредственно или же с jump'ом на код коррекции адреса перехода.
Так что не думаю что стоило здесь приводить подобные листинги, поскольку разговор шел о МС С++ в частности, и в целом для х86, если не ошибаюсь.
EAT>>Его виртуальность возникает вследствие перекрытия имен, которая разрешается в пользу виртуальности метода наследника.
E>Bravo! You made my day!
Я рад что тебе понравилось. Зови в следующий раз, посмешу
E>>>Виртуальные базовые классы инициализируются только из конструктора most derived class. EAT>>Я сказал про параметр, вследствие равенства нулю которого вызова конструктора базового класса вообще не происходит.
E>Прочитав учебник, Вы осознаете смысл сказанного выше. Если же Вы принципиально не читаете ничего, кроме ассемблерных листингов, помедитируйте над результатом трансляции следующего примера.
За это спасибо. Это как раз то, что мне было нужно. Ответ прозвучал и раньше, но боюсь я его не сразу понял.
Так что иду к стандартам по виртуальному наследованию и не только