Помогите Исправить Ошибки в Программе

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Airhand писал(а):Для int-а - это 0.
Т.е., если я в классе объявлю поле типа int, и в контрукторе не проинициализирую его, и не сделаю присвоения, то его значение ГАРАНТИРОВАННО будет равно нулю?
Свежо предание, да верится с трудом (c)
MiReQ
Сообщения: 28
Зарегистрирован: 13 май 2009, 13:06
Откуда: Россия, Пермь
Контактная информация:

BBB
По-моему, это будет зависеть от компилятора... Или я не прав?..
Гарантии будет давать конкретный разработчик, он ведь тоже может отойти от стандартов...

P.S. Все-таки, языки высокого уровня так неоднозначны... ASM рулит :)
Аватара пользователя
Airhand
Сообщения: 239
Зарегистрирован: 06 окт 2005, 16:21
Откуда: Dnepropetrovsk

BBB писал(а):Т.е., если я в классе объявлю поле типа int, и в контрукторе не проинициализирую его, и не сделаю присвоения, то его значение ГАРАНТИРОВАННО будет равно нулю?
Свежо предание, да верится с трудом (c)
Да, так по крайней мере написано в 3 стандарте. Если компилятор полностью поддерживает стандарт анси, то инты проинитятся в ноль где-то в конструкторе.
Оптимизация по скорости:
#define while if
Оптимизация по размеру:
#define struct union
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

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

Наверное, стоит остановится на этих понятиях несколько подробней, чтобы стало понятно в чём отличие проставления значения в списке инициализации и в теле конструктора.

У каждого типа в языке С++ обязательно есть три следующие специальных метода. Они есть в любом случае, если при создании своего класса вы их не укажите, они всё равно будут существовать, так как будет сгенерированы компилятором автоматически. Для системных типов они тоже существуют, но неявно и всегда заинланены. Сущности следующие:

- Конструктор по умолчанию.
- Конструктор копирования.
- Оператор присваивания.

Отличие в следующем:
1. В случае проставления значения в списке инициализации мы имеем только один вызов конструктора копирования.
2. В случае проставления значения в теле конструктора, мы имеем сначала вызов конструктора по умолчанию, затем вызов оператора присваивания.

Понятно, что для пользовательских типов (иными словами классов), это отличие важно: мы по разному можем реализовать эти три метода и разница вполне можеть быть критичной.

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

- Конструктор по умолчанию - пустое тело.
- Конструктор копирования - побитовое копирование.
- Оператор присваивания - побитовое копирование.

Таким образом, для встроенных типов в случае 2 мы имеем на один вызов больше, но результат, гарантированно, тот же самый. Более того, за счёт оптимизации пустое тело даже не будет вызвано, так что разница просто теряется... но... но, не стоит спешить с выводами, господа. Чем же лучше использования списка инициализации для встроенных типов? Вот доводы:

1. У нас нет никакой гарантии, что разработчик очередного компилятора не поменяет подход и не сделает так, что два случая будут давать разный результат.
2. Используя проставление значения в теле конструктора мы рушим стиль программирования: логически одинаковые вещи должны реализоваться одинаковыми средствами. Делать различия для встроенных и не встроенных типов неверно.
3. Академичность подхода прежде всего. Делая присваивание вместо инициализации вы показываете, что не понимаете разницу между ними. Такой код выглядит непрофессионально.

Теперь по поводу ноля, в качестве значения по умолчанию для int. Airhand , мы уже с тобой по этому поводу один раз беседовали и я тебя поправлял. Сейчас ты даёшь ту же самую неверную информацию людям.

На самом деле, дело обстоит так. Из написанного ранее должно быть ясно, что для встроенных типов не существует значения по умолчанию, так как их конструктор по умолчанию - пустое тело. Таким образом полагаться или не полагаться на то, что там ноль мы можем только в одном случае - если знаем как был создан объект. Оказывается у нас есть определённые гарантии стандарта. Стандарт гарантирует, что вся статическая память (в которой располагаются глобальные и статические переменные) будет проинициализирована нулями ещё до входа в main. Таким образом, если, к примеру, объект нашего класса будет создан глобально, то в его int мембере действительно будет 0. Но не потому, Airhand , что был вызван конструктор по умолчанию для этой int переменой, а потому, что переменная попадает в обнулённую ранее память. В случае, если наш объект создадут на стеке или в динамической памяти - там будет мусор и только мусор, можешь проверить.

Опять-таки, полагаться на то, как будет объект создан (даже если у нас есть такие даные) - это верх корявости и непрофессионализма. Переменные встроенных типов следут инициализировать явно в любом случае, независимо от места их размещения в памяти.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Airhand писал(а):Да, так по крайней мере написано в 3 стандарте. Если компилятор полностью поддерживает стандарт анси, то инты проинитятся в ноль где-то в конструкторе.
Romeo писал(а):Стандарт гарантирует, что вся статическая память (в которой располагаются глобальные и статические переменные) будет проинициализирована нулями ещё до входа в main. Таким образом, если, к примеру, объект нашего класса будет создан глобально, то в его int мембере действительно будет 0. Но не потому, Airhand , что был вызван конструктор по умолчанию для этой int переменой, а потому, что переменная попадает в обнулённую ранее память. В случае, если наш объект создадут на стеке или в динамической памяти - там будет мусор и только мусор, можешь проверить.
Проверил на MinGW:

Код: Выделить всё

#include <iostream>

using namespace std;  // for cout, cerr

class tWithInt {
   private :
     int m_int;
   public :
     tWithInt (void) {};
     int Get_m_int (void) { return m_int; };
};

tWithInt glo_WithInt;

int main (int argc, char** argv)
{
  tWithInt oWithInt;
  tWithInt * pWithInt;
 //----------------------------------

  cout << "glo_WithInt.m_int: " <<  glo_WithInt.Get_m_int () << endl;

  cout << "oWithInt.m_int: " <<  oWithInt.Get_m_int () << endl;

  pWithInt = new tWithInt ();
  cout << "pWithInt->m_int: " <<  pWithInt->Get_m_int () << endl;
  delete pWithInt;

  return (0);
};  // main
Результат:
glo_WithInt.m_int: 0
oWithInt.m_int: 2147332096
pWithInt->m_int: 0


Т.е. В ОБЩЕМ СЛУЧАЕ зануления поля класса типа int не происходит (т.е. Airhand не прав).
Для глобалного экземпляра гласса, действительно, получилось 0. Для хипа почему-то (к моему удивлению) тоже 0.

Romeo, спасибо за подробное опсиание происходящих процессов при создании обеъктов. Всегда полезно время от времени "устаканивать" имеющиеся в голове знания, вновь поторив теорию :)
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

&quot писал(а):. Для хипа почему-то (к моему удивлению) тоже 0.
Стандарт даёт гарантию только о том, что должна быть занулена статическая область памяти. Всё остальное на усмотрение компилятора. Часто компиляторы оптимизируют работу программы не зануюляя остальные типы памяти.

Кстати, ради интереса подёргай в цикле динамическую память, создавая объект, изменяя его и удаляя. Я более чем уверен, что компилятор, который ты используешь занулил всю хипу в самом начале, а в процессе работы программы выделенную с помощью new память он уже не зануляет (иначе будут ощутимые затраты), а отдаёт её as is. Таким образом, тебе могло просто повести, что ты сделал new первым, потому там и ноль. Ещё раз говорю, гарантии стандарта, что там будет ноль ВСЕГДА нету.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Romeo, вот потому я и написал, что "к моему удивлению".
Аватара пользователя
Airhand
Сообщения: 239
Зарегистрирован: 06 окт 2005, 16:21
Откуда: Dnepropetrovsk

BBB писал(а):Проверил на MinGW:

Код: Выделить всё

#include <iostream>
 
using namespace std;  // for cout, cerr
 
class tWithInt {
   private :
     int m_int;
   public :
     tWithInt (void) {};
     int Get_m_int (void) { return m_int; };
};
 
tWithInt glo_WithInt;
 
int main (int argc, char** argv)
{
  tWithInt oWithInt;
  tWithInt * pWithInt;
 //----------------------------------
 
  cout << "glo_WithInt.m_int: " <<  glo_WithInt.Get_m_int () << endl;
 
  cout << "oWithInt.m_int: " <<  oWithInt.Get_m_int () << endl;
 
  pWithInt = new tWithInt ();
  cout << "pWithInt->m_int: " <<  pWithInt->Get_m_int () << endl;
  delete pWithInt;
 
  return (0);
};  // main

Результат:
glo_WithInt.m_int: 0
oWithInt.m_int: 2147332096
pWithInt->m_int: 0

Т.е. В ОБЩЕМ СЛУЧАЕ зануления поля класса типа int не происходит (т.е. Airhand не прав).
Для глобалного экземпляра гласса, действительно, получилось 0. Для хипа почему-то (к моему удивлению) тоже 0.
А что MinGW полностью поддерживает 3 стандарт ? Может там просто не происходит вызова конструктора. Попробуй на вижуале.
Оптимизация по скорости:
#define while if
Оптимизация по размеру:
#define struct union
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Airhand писал(а): Может там просто не происходит вызова конструктора. Попробуй на вижуале.
Не происходит вызова конструктора ЧЕГО?
Аватара пользователя
Airhand
Сообщения: 239
Зарегистрирован: 06 окт 2005, 16:21
Откуда: Dnepropetrovsk

BBB писал(а):Не происходит вызова конструктора ЧЕГО?
Класса конечно, ведь в нём происходит инициализация членов.
Оптимизация по скорости:
#define while if
Оптимизация по размеру:
#define struct union
Ответить