вопрос к знатокам C++
vc6 parent
могу даже обьяснить почему, если интересно.
Нехорошо в конструкторе виртульные функции вызывать. Да и смысла в этом нет абсолютно никакого - естественно должна вызываться родная функция.
Это из-за того, что функция виртуальная.
А если убрать virtual в конструкторе B, что-то изменится?
В конструкторе и деструкторе, даже если он виртуальный, функции класса всегда вызываются напрямую. Их виртуальность роли не играет. Объясняется это тем очевидным соображением, что класс-child в этот момент уже разрушен или еще не построен.
Почему так - очевидно. Если не очевидно - почитай книжки, гугль, в конце-концов.
конструкторы предка выполняются первыми.
Вторая истина:
конструктор не может быть виртуальным.
Поэтому выполняется сначала конструктор предка( т.е. А ) с побочным эффектом( A { foo; } а затем выполняется тело конструктора класса В( т.е. ничего не выполняется ).
Причем, на основании второй истины выполняется именно foo класса А.
п. 12.7.3Достаточно было бы этого
Было бы интереснее, если бы была иерархия из 3-х классов
Чтобы в функции A вызвать виртуальную функцию B, виртуальность функции A не требуется.
Я просто к тому что при вызове конструктора класса А о другой виртуальной функции foo ничего не известно, поэтому она и вызовется.
Но это не освобождает от незнания второй истины
а если то же самое проделать с деструкторами ?
проделай.
Parent
> о другой виртуальной функции foo ничего не известно
Вообще говоря это всегда так происходит с виртуальными функциями.
ты не прав
При вызове конструктора В вызывается контруктор А.
А при внутренних вызовах метода вызываются методы, которые находяться в том же классе, где и их вызвали...
Т.е. , происходит так:
B::B
A::A
A::foo
жаль Лориена, теперь каждый будет писать, что выводится Parent. Это уже не оригинально.
Пусть кто-нибудь напишет нечто другое
Обрати внимание на слово virtual.
А при внешних, типа
B pB = new B;
A* pA = B;
Вызовы типа
pA->foo; - это внешний вызов...
Первый раз слышу о "внутренних" и "внешних" вызовах, честно.
....
_ZN1BC1Ev: ; B::B
.LFB12:
pushl %ebp
.LCFI3:
movl %esp, %ebp
.LCFI4:
subl $8, %esp
.LCFI5:
movl 8(%ebp %eax
movl %eax, (%esp)
call _ZN1AC2Ev ; call A::A
movl 8(%ebp %eax
movl $_ZTV1B+8, (%eax) ; fill VMT
leave
ret
....
_ZN1AC2Ev: ; A::A
.LFB13:
pushl %ebp
.LCFI6:
movl %esp, %ebp
.LCFI7:
subl $8, %esp
.LCFI8:
movl 8(%ebp %eax
movl $_ZTV1A+8, (%eax) ; fill VMT
movl 8(%ebp %eax
movl %eax, (%esp)
call _ZN1A3fooEv ; call A::foo
leave
ret
....
В этом случае дело именно в конструкторе, конструктор объекта A вызывается всегда для объекта A. То, что он внутри объекта B - совпадение.
class X
{
public :
virtual void g (int j) { cout << "X::g(" << j << ")\n"; }
void g { cout << "X::g" << '\n'; }
};
class Y : public X
{
public :
void g (int j) { cout << "Y:;g(" << j << ")\n"; }
void g { cout << "Y::g" << '\n'; }
};
void d (X * px, Y * py)
{
px->g (2);
px-> g ;
py -> g (2);
py-> g ;
}
void f
{
X x; Y y; X * px; Ó * py;
px = &x; py = &y;
d (px, py) ;
px = py;
d(px, py) ;
d(py,py) ;
}
Тут скажи, что выведет...
При вызове f...
Это просто для саморазвитие
1.cpp:4:59: missing terminating " character
1.cpp:9: error: base class `X' has incomplete type
1.cpp:11: error: stray '\' in program
1.cpp:11:51: missing terminating " character
1.cpp:25: error: parse error before `#' token
1.cpp:36: error: syntax error at end of input
Тут main'а нет
Это был не с++ !
В ворде просто "<<" сделано как открывающая скобка...
Мейн сами напишите
void main
{
f;
}
В частности, из кода видно, что указатель на VMT выставляется после инициализации
parent класса и перед кодом конструктора.
Таким образом, внутри самого конструктора можно обращаться только к методам класса.
Child класс к этому моменту не проинициализирован, поэтому его методы вызывать было бы неправильно.
#include <iostream.h>
class X
{
public:
virtual void g (int j) { cout << "X::g(" << j << ")\n"; }
void g { cout << "X::g" << '\n'; }
};
class Y : public X
{
public:
void g (int j) { cout << "Y:;g(" << j << ")\n"; }
void g { cout << "Y::g" << '\n'; }
};
void d (X * px, Y * py)
{
px->g (2);
px-> g ;
py -> g (2);
py-> g ;
}
void f
{
X x; Y y; X * px; Y * py;
px = &x; py = &y;
d (px, py) ;
px = py;
d(px, py) ;
d(py,py) ;
}
void main
{
f;
}
Сперва подумайте. А потом свертесь с компилятором
Сверился, совпало.
Это объясняет только наполовину. Например, как быть в следующем примере:
#include <stdio.h>
class A {
public:
A { callFoo; }
void callFoo { foo; }
virtual void foo { printf("Parent\n"); }
};
class B : public A {
public:
B : A { }
virtual void foo { printf("Child\n"); }
};
int main
{
B b;
b.callFoo;
return 0;
}
Здесь в конструкторе вызывается метод callFoo который явно вызывает виртуальный метод:
_ZN1A7callFooEv:
[skipped]
movl (%edx %eax
call *%eax
leave
ret
То есть подобное поведение свидетельствует о том что, на время вызова конструктора базового класса в объект ставится таблица виртуальных методов базового класса, затем когда он уже предок инициализирова, объекту прописывает его "родная" таблица виртуальных методов.
В пункте 17.2.3 написано (цитирую Draft 2):
When a virtual function is called directly or indirectly from a constructor (including fromНасколько я понимаю, иного способа реализовать выделенное жирным шрифтом нет.
the mem-initializer for a data member) or from destructor, and the object to which is the call
applies is the object under construction or destruction, the function is called is the one defined
in the constructor or destructor's own class or in one of its bases, but not a function overriding
it in a class derived from the constructor or destructor's class, or overriding it in one of the
other base classes of the most derived object.
То есть при наличии вложенной цепочки конструкторов типа B::B : A C::C : B будет по очереди меняться таблица виртуальных методов на A, B, C?
Кстати, если вы тыкаете ссылками на стандарт, скажите где вы его взяли, а то я ничего кроме draft-ов найти не могу.
> В частности, из кода видно, что указатель на VMT выставляется после инициализации
> parent класса и перед кодом конструктора.
я правильно понимаю, что подобная "переустановка" VMT положена по стандарту?
Исошный стандарт С++ первой редакции за 98 год был в сети.
вызов виртуаьлного метода - это вызов функции по адресу, лежащему в VMT и все.
в Паскале бывают
это как? приведи ассемблерный листинг внутреннего и внешнего вызова чтобы сравнить
или короткие и дальние
давно это было
на первом курсе
но то что вызов функции в одном сегменте и вызов функции из другого сегмента памяти в Паскале есть
и на АСМе тоже это можно написать
Но это все не относится к С++
Стандарт может не знать подобных тонкостей реализации вообще.
Если _такая_ реализация позволяет добиться поведения,
указанного в стандарте (в частности, процитированный тобой кусок значит она правильная.
это не то, я внутренние и внешние хочу
> это не то, я внутренние и внешние хочу
в дельфях можно взять указатель на метод класса
можешь считать вызов по такому указателю внешним
зы а ещё там виртуальные конструкторы есть
А у нас - водопровод, вот!
Оставить комментарий
Landstreicher
Объясните пожалуйста, что должна выдавать такая программа:У меня выводит Parent, хотя мне почему-то упорно кажется что должно выводить Child.
Проверялось на gcc версий 2.95.3, 3.0.4, 3.2, 3.2.3, 3.3, 3.3.2 prerelease - везде одно и то же.
У кого-нибудь есть под рукой другой компилятор?