Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> инициализация в конструкторе... 
:(
    Опции темы
wowka19
Дата 6.11.2012, 22:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Объясните пожалуйста разницу между приведенными ниже конструкторами.
Я понимаю, что объект в итоге проинициализируется одинаково, и что первый пример единственный если m_data - const.
И еще часто встречаю рекомендации использовать первый пример.
Но почему?

Меня интересуют не только эстетические моменты и случай с const, но и внутреннюю интерпретацию этого кода (то есть что скрывается под капотом)

Код

// example 1:
MyClass::MyClass() : m_data(0) {}


// example 2:
MyClass::MyClass() {m_data = 0;}


PM MAIL   Вверх
bsa
Дата 6.11.2012, 22:22 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



представь, что у тебя m_data имеет сложный тип (выделяет память в конструкторе и освобождает в деструкторе). Если ты поставишь во все методы класса Data логирование, то увидишь:
1.
Код
Data::Data(int)
MyClass::MyClass()
...

2.
Код
Data::Data() //перед входом в тело конструктора все члены класса должны быть проинициализированы
MyClass::MyClass() //входим в тело
Data::Data(int) //создается временный объект типа Data
Data::operator=(Data&) // вызывается оператор присваивания
Data::~Data() //уничтожается временный объект типа Data
...


Кстати, если у класса Data нет конструктора по умолчанию, то второй варианты у тебя не скомпилируется.

Чисто теоретически, можно предположить, что в для простых типов компилятору в первом случае будет легче оптимизировать инициализацию.
PM   Вверх
baldina
Дата 6.11.2012, 22:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



под капотом суть одно и то же, а разница, она же ошибка - тут
Цитата(wowka19 @  6.11.2012,  22:04 Найти цитируемый пост)
Я понимаю, что объект в итоге проинициализируется одинаково

дело в том, что m_data = 0; это присваивание, а не инициализация. инициализация произошла раньше (мусором).
а перед { это как раз инициализация. разница конечно тонкая, но это, например, единственный способ инициализировать ссылки, а так же члены, которым нельзя присваивать - упомянутые вами константы и объекты с закрытым оператором присваивания.

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

Добавлено @ 22:42
Цитата(baldina @  6.11.2012,  22:40 Найти цитируемый пост)
под капотом суть одно и то же

bsa все подробно расписал))) а компилятор все это соптимизирует в nop.

Это сообщение отредактировал(а) baldina - 6.11.2012, 22:43
PM MAIL   Вверх
wowka19
Дата 7.11.2012, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



Цитата(baldina @  6.11.2012,  22:40 Найти цитируемый пост)
под капотом суть одно и то же, а разница, она же ошибка - тут
Цитата(wowka19 @  6.11.2012,  22:04 )
Я понимаю, что объект в итоге проинициализируется одинаково

я имел ввиду, что после отработки конструктора в обоих случаях мы получим идентичные объекты.

bsa, не совсем понял.

Цитата(baldina @  6.11.2012,  22:40 Найти цитируемый пост)
а компилятор все это соптимизирует в nop.

что это значит?


Это сообщение отредактировал(а) wowka19 - 7.11.2012, 12:29
PM MAIL   Вверх
Dem_max
Дата 7.11.2012, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 4
Всего: 39



Цитата

я имел ввиду, что после отработки конструктора в обоих случаях мы получим идентичные объекты.

Не идентичные объекты, а значения, но это для простых объектов у которых используется оператор присваивания (тут нет никакой разницы как ты сделаешь). Для сложных объектов инициализация только так
MyClass::MyClass() : m_data(0) {}

Это сообщение отредактировал(а) Dem_max - 7.11.2012, 12:33


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
baldina
Дата 7.11.2012, 14:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



Цитата(wowka19 @  7.11.2012,  12:08 Найти цитируемый пост)
Цитата(baldina @  6.11.2012,  22:40 )
а компилятор все это соптимизирует в nop.

что это значит?

nop - "нет операции", пусто
bsa расписал логику работы конструктора, в режиме отладки так и будет, но поскольку функции пустые, компилятор преобразует это в "ничто", не будет вызова пустых функций. даже копирования временных объектов скорее всего не будет.
PM MAIL   Вверх
wowka19
Дата 7.11.2012, 14:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



ладно... похоже без спец. литературы не обойтись. Посоветуйте пожалуйста книжку, ясно и подробно объясняющую моменты подобные в этой теме. то есть не какой нибудь "C++ за 21 день/24 часа", а что-то фундаментальное.
PM MAIL   Вверх
baldina
Дата 7.11.2012, 15:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



любою толстую возьми с нерусской фамилией автора  smile 
Страуструп, Прата, Лафоре - у каждого из них есть толстые книжки типа "язык программирования С++"

Добавлено через 2 минуты и 43 секунды
если мои слова про оптимизацию компилятором сбивают с толку, просто игнорируйте их. это не так важно для понимания.
важнее семантика языка (т.е. то что bsa написал) и правила (относительно инициализации констант, ссылок, прочих типов без доступного оператора присваивания)
PM MAIL   Вверх
wowka19
Дата 7.11.2012, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



ну хотелось бы поконкретнее, исходя из собственного опыта, а не просто "толстые книжки". у того же страуструпа этих толстых книжек не одна.

на счет оптимизации я как раз понимаю - тут чисто здравая логика помогает.
PM MAIL   Вверх
feodorv
Дата 7.11.2012, 17:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Вопрос, кстати хороший. 
Цитата(baldina @  6.11.2012,  23:40 Найти цитируемый пост)
дело в том, что m_data = 0; это присваивание
 Действительно, возникает большая путаница в этом вопросе.

Чем кардинально отличается инициализация от присваивания. Присваивание должно осуществляется на уже инициализированной памяти, а инициализация происходит на памяти, содержимое которой заранее не известно (мусор), да и неважно. Для встроенных типов (int,char * etc) большой разницы нет, поскольку что в случае инициализации, что в случае присваивания, новое значение переменной затрёт старое, и всё. Для сложных типов всё сложно))) Инициализация просто определит (ту мусорную) память, отведённую под переменную этого типа, значащими величинами (в том числе будет проинициализирована таблица виртуальных вызовов). Присваивание переопределит память (которая считается уже проинициализированной адекватными значениями), отведённую под переменную этого типа. И вот здесь как раз таятся подводные камни.

Рассмотрим довольно часто встречающийся пример:
Код

struct my_data
{
  std::string string;
  int status;
};

int main()
{
  my_data myData;

  // Проинициализируем поля структуры
  myData.string = "Evergreen";
  myData.status = 0;

  ...


Вот несмотря на то, что программист написал про "инициализацию", на самом деле происходит присваивание. И что получается в итоге? 
Чтобы присвоить новое значение строке myData.string нужно избавиться от старого (иначе - утечка памяти). Но проблема в том, что старого значения не существует (оно мусорное, непроинициализированное никак). Что делать? Как раз нужно предварительно проинициализировать переменную myData (в этом случае содержимое памяти, отведённое под саму переменную в начале выполнения программы неважно, оно будет заполнено корректными значениями), а не пытаться ей присвоить какое-то значение (в этом случае содержимое памяти, отведённой под myData, уже играет роль). Итог: нужно писать конструктор структуры my_data (и деструктор тоже).

В реальности всё ещё сложнее. "Операция присваивания" приобретает статус "оператора присваивания" (то есть по специальным правилам вызывается заранее определённая функция под названием operator=), вызываются нужные конструкторы и деструкторы, bsa всё здорово описал.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
baldina
Дата 7.11.2012, 18:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



подумалось, удобно объяснять на шаблонном классе

Код

template <typename T, typename U>
class Class {
  T data;
  Class (const U& d) { data = d; }
};


когда компилятор не сможет инстанциировать этот шаблон (т.е. создать конкретный класс для конкретного типа T)?
иными словами, какие условия накладываются на тип T? Это вытекает из последовательности вызовов, описанных bsa
1. Наличие public конструктора по-умолчанию вида T()  (возможно, сгенерированного компилятором по умолчанию)
2. Наличие преобразования из U в T, например конструктора T(U), если U и T различны
3. Наличие public T& operator=(const T&); (возможно, сгенерированного компилятором по умолчанию)
4. Наличие public конструктора копирования T(const T&); для копирования временного объекта, если U и T разные типы, либо для копирования аргумента, если U и T один тип

под эти условия подходят все встроенные типы, именно поэтому например для int создается ощущение, что разницы нет.

В случае кода
Код

template <typename T, typename U>
class Class {
  T data;
  Class (const U& d) : data(d) { }
};

требование лишь одно, наличие соответствующего public конструктора T(const U&); (в случае, если Т и U один тип, это будет конструктор копирования T(const T&);)

Так что последний способ короче, более общий (работает в большем числе случаев, в т.ч. для констант и ссылок), и вдобавок производительней, т.к. операций в общем случае производится меньше. Последнее имеет значение, если такие объекты создаются довольно интенсивно, а T имеет не простые конструктор и деструктор.
PM MAIL   Вверх
wowka19
Дата 7.11.2012, 20:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 30.11.2011

Репутация: нет
Всего: нет



голова распухла - шапка не налазит)



кажись моя проблема в этом:
Цитата(feodorv @  7.11.2012,  17:36 Найти цитируемый пост)
Инициализация просто определит (ту мусорную) память, отведённую под переменную этого типа, значащими величинами


Я думал, что любой мусор будет являться значащей(адекватной) величиной любого типа.
На примере short - диапазон этого типа 65536 значений, что является максимальным количеством состояний памяти размером 16 бит, отведенных под этот тип. Другими словами любой мусор в любом 16-битном участке памяти является корректным short.
Или я не прав?

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

class Class1
{
        int m_data;
public:
        Class1() : m_data(0) {}
}

class Class2
{
        int m_data;
public:
        Class2() {m_data = 0;}
}


Это сообщение отредактировал(а) wowka19 - 7.11.2012, 21:00
PM MAIL   Вверх
feodorv
Дата 7.11.2012, 22:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(wowka19 @  7.11.2012,  21:52 Найти цитируемый пост)
На примере short - диапазон этого типа 65536 значений, что является максимальным количеством состояний памяти размером 16 бит, отведенных под этот тип. Другими словами любой мусор в любом 16-битном участке памяти является корректным short.

Вопрос, что считать "корректным". На мой взгляд, мусор считаться корректным не может. Но в приведённом Вами коде с Class2 изначально мусорное значение поля m_data роли не играет. И именно потому, что тип m_data является простым встроенным типом, для которого что инициализация, что присвоение - всё едино. Но как только мы начинаем иметь дело со сложными типами, инициализация начинает резко отличаться от присвоения.

Цитата(wowka19 @  7.11.2012,  21:52 Найти цитируемый пост)
дадут идентичный бинарный выхлоп (то есть будут являться одним и тем же набором команд/инструкций):

Результат они должны дать одинаковый, но вот чтобы прямо-таки одинаковый бинарный код - не уверен.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
baldina
Дата 7.11.2012, 23:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



Цитата(feodorv @  7.11.2012,  22:53 Найти цитируемый пост)
Вопрос, что считать "корректным". На мой взгляд, мусор считаться корректным не может.

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

Добавлено через 3 минуты и 21 секунду
Цитата(wowka19 @  7.11.2012,  20:52 Найти цитируемый пост)
отсюда и мнение, что конструкторы (ниже) дадут идентичный бинарный выхлоп

для простых типов да. но не для всех.
т.к. мы говорим об ООП, под объектом может быть нечто далекое от тривиальных встроенных типов.
это элемент культуры: если будешь всегда инициализировать до тела конструктора, в менее очевидных случаях вероятность ошибки меньше.
PM MAIL   Вверх
NoviceF
Дата 9.11.2012, 00:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Цитата(wowka19 @  7.11.2012,  16:55 Найти цитируемый пост)
ну хотелось бы поконкретнее, исходя из собственного опыта, а не просто "толстые книжки". у того же страуструпа этих толстых книжек не одна.


Рекомендую -  Скотт Мэйерс "Эффективное использование C++" Третье издание (2006 год), "правило 4: Прежде чем использовать объекты, убедитесь, что они инициализированы".
PM MAIL   Вверх
Remiznik
Дата 21.11.2012, 17:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 379
Регистрация: 30.4.2005

Репутация: 1
Всего: 1



читаю Солтер Н. С++ для профессионалов. 2006 год. И вот на что наткнулся по поводу данной темы 

Цитата

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

1 const - члены данных - Нельзя законно присвоить значение const - переменной после ее создания. Она должна быть инициализирована в момент создания.
2 Ссылочные типы данных - Ссылки не могут существовать, не ссылаясь на что-либо.
3 Объектные члены данных, для которых не существует конструктора по умолчанию - С++ попытается инициализировать объекты-члены данных, используя конструктор по умолчанию. Если таково такового нет то инициализация невозможна.
4 Базовые классы без конструкторов по умолчанию.


Модератор: для цитирований следует использовать тег QUOTE, а не CODE.
PM MAIL   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1452 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.