вопрос к знатокам C++
vc6 parent
Parent 
могу даже обьяснить почему, если интересно.

могу даже обьяснить почему, если интересно.
Нехорошо в конструкторе виртульные функции вызывать. Да и смысла в этом нет абсолютно никакого - естественно должна вызываться родная функция.
Объясни.
Это из-за того, что функция виртуальная.
А если убрать virtual в конструкторе B, что-то изменится?
Это из-за того, что функция виртуальная.
А если убрать virtual в конструкторе B, что-то изменится?
В конструкторе и деструкторе, даже если он виртуальный, функции класса всегда вызываются напрямую. Их виртуальность роли не играет. Объясняется это тем очевидным соображением, что класс-child в этот момент уже разрушен или еще не построен.
Это поведение сформулировано в стандарте языка, п. 12.7.3
Почему так - очевидно. Если не очевидно - почитай книжки, гугль, в конце-концов.
Почему так - очевидно. Если не очевидно - почитай книжки, гугль, в конце-концов.
Первая истина:
конструкторы предка выполняются первыми.
Вторая истина:
конструктор не может быть виртуальным.
Поэтому выполняется сначала конструктор предка( т.е. А ) с побочным эффектом( A { foo; } а затем выполняется тело конструктора класса В( т.е. ничего не выполняется ).
Причем, на основании второй истины выполняется именно foo класса А.
конструкторы предка выполняются первыми.
Вторая истина:
конструктор не может быть виртуальным.
Поэтому выполняется сначала конструктор предка( т.е. А ) с побочным эффектом( A { foo; } а затем выполняется тело конструктора класса В( т.е. ничего не выполняется ).
Причем, на основании второй истины выполняется именно foo класса А.
п. 12.7.3Достаточно было бы этого

Было бы интереснее, если бы была иерархия из 3-х классов 

Чтобы в функции A вызвать виртуальную функцию B, виртуальность функции A не требуется.
прицепился все-таки 
Я просто к тому что при вызове конструктора класса А о другой виртуальной функции foo ничего не известно, поэтому она и вызовется.
Но это не освобождает от незнания второй истины

Я просто к тому что при вызове конструктора класса А о другой виртуальной функции foo ничего не известно, поэтому она и вызовется.
Но это не освобождает от незнания второй истины

а если то же самое проделать с деструкторами ?
проделай.
проделал
Parent
Parent
> при вызове конструктора класса А
> о другой виртуальной функции foo ничего не известно
Вообще говоря это всегда так происходит с виртуальными функциями.
> о другой виртуальной функции foo ничего не известно
Вообще говоря это всегда так происходит с виртуальными функциями.

ты не прав 

Выводиться "парент". И это понятно...
При вызове конструктора В вызывается контруктор А.
А при внутренних вызовах метода вызываются методы, которые находяться в том же классе, где и их вызвали...
Т.е. , происходит так:
B::B
A::A
A::foo
При вызове конструктора В вызывается контруктор А.
А при внутренних вызовах метода вызываются методы, которые находяться в том же классе, где и их вызвали...
Т.е. , происходит так:
B::B
A::A
A::foo

жаль Лориена, теперь каждый будет писать, что выводится Parent. Это уже не оригинально.

Пусть кто-нибудь напишет нечто другое

Обрати внимание на слово virtual.
Я же и говорю, что при внутренних вызовах вызывается ...
А при внешних, типа
B pB = new B;
A* pA = B;
Вызовы типа
pA->foo; - это внешний вызов...
А при внешних, типа
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: error: stray '\' in program
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
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'а нет
Тут main'а нет

Это был не с++ !
Это я с ворда скопировал, там чуть ошибок поправить надо...
В ворде просто "<<" сделано как открывающая скобка...
Мейн сами напишите
void main
{
f;
}
В ворде просто "<<" сделано как открывающая скобка...
Мейн сами напишите
void main
{
f;
}
> Вот _как_ это работает:
В частности, из кода видно, что указатель на VMT выставляется после инициализации
parent класса и перед кодом конструктора.
Таким образом, внутри самого конструктора можно обращаться только к методам класса.
Child класс к этому моменту не проинициализирован, поэтому его методы вызывать было бы неправильно.
В частности, из кода видно, что указатель на 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;
}
Сперва подумайте. А потом свертесь с компилятором

Сверился, совпало.
> В конструкторе и деструкторе, даже если он виртуальный, функции класса всегда вызываются напрямую
Это объясняет только наполовину. Например, как быть в следующем примере:
Здесь в конструкторе вызывается метод callFoo который явно вызывает виртуальный метод:
То есть подобное поведение свидетельствует о том что, на время вызова конструктора базового класса в объект ставится таблица виртуальных методов базового класса, затем когда он уже предок инициализирова, объекту прописывает его "родная" таблица виртуальных методов.
В пункте 17.2.3 написано (цитирую Draft 2):
То есть при наличии вложенной цепочки конструкторов типа B::B : A C::C : B будет по очереди меняться таблица виртуальных методов на A, B, C?
Кстати, если вы тыкаете ссылками на стандарт, скажите где вы его взяли, а то я ничего кроме draft-ов найти не могу.
Это объясняет только наполовину. Например, как быть в следующем примере:
#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 положена по стандарту?
> В частности, из кода видно, что указатель на VMT выставляется после инициализации
> parent класса и перед кодом конструктора.
я правильно понимаю, что подобная "переустановка" VMT положена по стандарту?
По поводу того, чтобы сделать функцию и там явно вызывать виртуальную функцию - это уже точно нужно смотреть в стандарт. Не удивлюсь, что разные реализации по разному смотрят на эти вещи.
Исошный стандарт С++ первой редакции за 98 год был в сети.
Исошный стандарт С++ первой редакции за 98 год был в сети.
внутренних и внешних вызовов не бывает, бывают только просто вызовы.
вызов виртуаьлного метода - это вызов функции по адресу, лежащему в VMT и все.
вызов виртуаьлного метода - это вызов функции по адресу, лежащему в VMT и все.
в Паскале бывают 

> в Паскале бывают
это как? приведи ассемблерный листинг внутреннего и внешнего вызова чтобы сравнить
это как? приведи ассемблерный листинг внутреннего и внешнего вызова чтобы сравнить

или короткие и дальние

давно это было
на первом курсе

но то что вызов функции в одном сегменте и вызов функции из другого сегмента памяти в Паскале есть

и на АСМе тоже это можно написать

Но это все не относится к С++

> я правильно понимаю, что подобная "переустановка" VMT положена по стандарту?
Стандарт может не знать подобных тонкостей реализации вообще.
Если _такая_ реализация позволяет добиться поведения,
указанного в стандарте (в частности, процитированный тобой кусок значит она правильная.
Стандарт может не знать подобных тонкостей реализации вообще.
Если _такая_ реализация позволяет добиться поведения,
указанного в стандарте (в частности, процитированный тобой кусок значит она правильная.
> или короткие и дальние
это не то, я внутренние и внешние хочу
это не то, я внутренние и внешние хочу

>> или короткие и дальние
> это не то, я внутренние и внешние хочу
в дельфях можно взять указатель на метод класса
можешь считать вызов по такому указателю внешним
зы а ещё там виртуальные конструкторы есть
> это не то, я внутренние и внешние хочу
в дельфях можно взять указатель на метод класса
можешь считать вызов по такому указателю внешним

зы а ещё там виртуальные конструкторы есть
А у нас в квартире газ!
А у нас - водопровод, вот!
А у нас - водопровод, вот!
вобщем, длительные поиски увенчались успехом, кому надо берите:
http://lorien.local/pub/docs/c++std/
http://lorien.local/pub/docs/c++std/
Оставить комментарий
Landstreicher
Объясните пожалуйста, что должна выдавать такая программа:У меня выводит Parent, хотя мне почему-то упорно кажется что должно выводить Child.
Проверялось на gcc версий 2.95.3, 3.0.4, 3.2, 3.2.3, 3.3, 3.3.2 prerelease - везде одно и то же.
У кого-нибудь есть под рукой другой компилятор?