LINUX.ORG.RU

Про наследование, конструкторы и виртуальные функции

 ,


0

0

Если написать так:

#include <iostream>
using namespace std;

class Father
{
 public:
	Father(int num){
      	 cout << "Папа сделан\n"<< num <<"\n";
	};
	virtual int badBoy(void){
	 return 22;
	};
};

class Son: public Father
{
 public:
	Son(void):Father(badBoy()){
	 cout << "Сын сделан\n";
	};
};

int main()
{
 Son a; 
}
И скомпилировать ,то во всех дистрибутивах(проверял на CentOS 5.9) данная поделка будит работать как задумывалось, а в ArchLinux отвалится с segfault. Почему так? А самое главное как это можно поправить не вмешиваясь в код?


Попробуй добавить в конец функции main()

return 0;

anonymous
()

Вот тут

Son(void):Father(badBoy())
вызывается метод Son::badBoy() до того, как будет создан не только сам объект, но и его предок. Вопрос скорее, какого хрена это иногда работает.

unC0Rr ★★★★★
()

Виртуальные функции в конструкторе дергать нехорошо. Особенно в списке инициализации :)

yoghurt ★★★★★
()

Вот еще интересное наблюдение:

[age@lab-laptop Sources]$ g++ test.cc -o test -Wall -Wextra -O0
[age@lab-laptop Sources]$ ./test 
Segmentation fault (core dumped)
[age@lab-laptop Sources]$ g++ test.cc -o test -Wall -Wextra -O1
test.cc: In function ‘int main()’:
test.cc:18:26: warning: ‘a.Son::<anonymous>.Father::_vptr.Father’ is used uninitialized in this function [-Wuninitialized]
  Son(void):Father(badBoy()){
                          ^
test.cc:25:6: note: ‘a’ was declared here
  Son a; 
      ^
[age@lab-laptop Sources]$ ./test 
Segmentation fault (core dumped)
[age@lab-laptop Sources]$ g++ test.cc -o test -Wall -Wextra -O2
[age@lab-laptop Sources]$ ./test 
Папа сделан
22
Сын сделан

age
()

во всех дистрибутивах(проверял на CentOS 5.9)

Давно центос стал всеми дистрибутивами?

yoghurt ★★★★★
()
Ответ на: комментарий от age

test.cc:18:26: warning: ‘a.Son::<anonymous>.Father::_vptr.Father’ is used uninitialized in this function [-Wuninitialized]

О чём и речь

yoghurt ★★★★★
()

Effective C++, Third Edition by Scott Meyers

Item 9: Never call virtual functions during construction or destruction.

geometer
()
Ответ на: комментарий от yoghurt

Гента свеженькая - работает. Выходит где GCC тухлее чем 4.6 - там работает.. есть подозрение что и в 4.7 работает. Такое чувство, что это фитча, которую бережно хранили...

naszar
() автор топика
Ответ на: комментарий от naszar

суть вопроса в том, почему это ИНОГДА РАБОТАЕТ

x0r ★★★★★
()

Это undefined behavior. В такой ситуации поведение программы остаётся целиком на совести компилятора и конечно никто не гарантирует что оно не поменяется от версии к версии.

KblCb ★★★★★
()
Ответ на: комментарий от age

Рано я обрадовался, мегаоптимизация

gcc -O2
, работает только когда класс и вызов конструктора в одном объектном файле.. когда они в разных - снова segfault...

naszar
() автор топика
Ответ на: комментарий от naszar

вот я тебя не понимаю...

Каждый в этом треде сказал тебе, что поведение программы не определено и так писать не надо, а ты все равно упорствуешь и кричишь, что в новом гцц все поломали, а раньше все было отлично...

solovey ★★
()
Ответ на: комментарий от true_admin

да они все грешат, msvs тоже иногда даёт дельные варнинги, когда гцц и кланг молчат...

Stil ★★★★★
()

На самом деле, хотя строго говоря, данный код ошибочен и так писать конечно же не надо, _по_хорошему_ никакого сегфаулта здесь быть не должно. Ну и что, что мы вызываем виртуальный метод из нециализированного объекта: метод непосредственно к объекту не привязан: метод - это функция на описательном уровне привязанная к классу. А поскольку данный конкретный метод не работает с объектом непосредственно и к его переменным до инициализации не обращается, то и ошибки здесь быть не должно. Более того, эта виртуальная функция и вовсе должна быть соптимизирована до константы.

	call   0x40098e <Son::Son()>
	mov    edi,0x8
	call   0x4007a0 <_Znwm@plt>
	mov    QWORD PTR [rax],0x0
	mov    QWORD PTR [rbp-0x8],rax
	mov    rax,QWORD PTR [rbp-0x8]
	mov    QWORD PTR [rbp-0x10],rax
	mov    rax,QWORD PTR [rbp-0x8]
	lea    rdx,[rbp-0x10]
	mov    QWORD PTR [rax],rdx
	mov    rax,QWORD PTR [rbp-0x8]
	mov    rdi,rax
	call   0x400740 <_ZdlPv@plt>
	mov    eax,0x0
	leave

Вот дизассемблированный код. Само собой, не падает. Но поскольку приведённый вами пример ошибочен, могут возникать накладки из-за слишком слабой его оптимизации компилятором && неудачного порядка выделения памяти под объект: например, если объект класса ещё не создан и не инициализирован указатель на таблицу виртуальных функций, а виртуальная функция badBoy(void) уже вызывается, естесственно возникнет ошибка сегментации. Для того, чтобы разобраться, что произошло конкретно, приводите листинги дизассемблера для рабочего варианта и для нерабочего.

Имейте в виду, что так делать нельзя, из-за того, что методы обычно не тупо возвращают значение, а обращаются к переменным класса и выполняют не всегда предсказуемую компилятором работу: ошибка сегментации в данном случае почти гарантирована. Да и вообще, это косяк с т.з. архитектуры программы и общей логики: вызывать виртуальные методы в конструкторе.

next_time ★★★★★
()

http://www.parashift.com/c -faq-lite/inline-virtuals.html

Therefore the only time an inline virtual call can be inlined is when the compiler knows the «exact class» of the object which is the target of the virtual function call. This can happen only when the compiler has an actual object rather than a pointer or reference to an object. I.e., either with a local object, a global/static object, or a fully contained object inside a composite.

Ваш случай с virtual inline. Компилятор, зная тип для такого метода (Father), может сделать оптимизацию, а может и вынести тело метода в объектный файл, как повезёт. Как только перенесёте метод из заголовочника в другую единицу трансляции, то оптимизация невозможна и ошибка должна проявлять везде.

Dendy ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.