Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Инициализация в конструкторе класса


Автор: VFaraon 26.3.2007, 23:19
Вот читаю Р. Лафоре... Мне нравится. Очень много полезных мелочей описано. И попал я на такую информацию:

Код

...
class Counter
{
  private:
   unsigned int count;
  public:
   Counter() : count(0)
     { /*Пустое тело*/}
  void inc_count()
   {
 ......


И еще...

Цитата

... Вы, вероятно, ожидали, что это действие будет произведено в теле конструктора приблизительно следующим образом:
 
count()
   { count=0; }

  Такая форма записи не рекомендется, не смотря на то, что она не содержит ошибок. Инициализация в нашем примере происходит следующим образом:

count(): count(0)
 { }

.....

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


 "Такая форма записи не рекомендется" - Не мог бы ктото поподробней рассказать об этой ситуации?

"достаточно сложны" - И об этом... 


"Инициализация полей с помощью списка инициализации происходит до начала исполнения тела конструктора, что в некоторых ситуациях бывает важно" - Было бы оч хорошо, БОЛЬШЕ узнать об ЭТОМ механизме. Может ссылочку дадите или ктото сам попробует, а то уж больно интересный (для меня) вопрос smile



   

Автор: Daevaorn 26.3.2007, 23:28
Цитата(VFaraon @  27.3.2007,  00:19 Найти цитируемый пост)
 Не мог бы ктото поподробней рассказать об этой ситуации?

да. не рекомендуется. просто  по тому, что есть список инициализации
Цитата(VFaraon @  27.3.2007,  00:19 Найти цитируемый пост)
"достаточно сложны" - И об этом... 

это надо у автора спрашивать
Цитата(VFaraon @  27.3.2007,  00:19 Найти цитируемый пост)
"Инициализация полей с помощью списка инициализации происходит до начала исполнения тела конструктора, что в некоторых ситуациях бывает важно" - Было бы оч хорошо, БОЛЬШЕ узнать об ЭТОМ механизме. Может ссылочку дадите или ктото сам попробует, а то уж больно интересный (для меня) вопрос

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

Автор: Earnest 27.3.2007, 08:13
Цитата(VFaraon @  27.3.2007,  00:19 Найти цитируемый пост)
"достаточно сложны" - И об этом... 

Это автор делает вид, что он очень умный, а все остальные - ...

Одну причину Daevaorn назвал - некоторые члены можно проинициализировать только в списке инициализации (константы, ссылки), а также классы, не имеющие дефолт-конструктора.

Вторая - сложные члены данных (классы) сами инициализируются по умолчанию до входа в тело конструктора. Если добавить их инициализацию в список инициализации явно - будет вызван соответствующий конструктор, если нет - все равно будет вызван дефолт-конструктор. 
А потом ты присваиваешь им новое значение в теле конструктора и делаешь, таким образом, лишнюю работу. 

Не всегда это так уж страшно, а встроенные типы (int, double) вообще по умолчанию никак не инициализируются. 
Но для некоторых пользовательских типов это может означать выделение памяти под дефолт-значение, а затем удаление ее и выделение под новое значение. 

Так что инициализировать данные класса в списке инициализации - хорошая привычка.

Добавлено через 1 минуту и 33 секунды
Приведенный автором пример с count - абсолютно по барабану как писать...

Автор: KelTron 27.3.2007, 11:14
Цитата
Списки инициализации членов
Чтобы избавиться от этой проблемы, в C++ находится очередное применение символу : — для
создания списков инициализации членов. Так называется список спецификаций конструкторов,
разделенных занятыми и расположенных между сигнатурой конструктора и его телом.
Код

class Foo {
public:
Foo(char*);
};
class Bar : public Foo {
public:
Bar(char*);
};
class BarBar {
private:
Foo f;
int x;
public:
BarBar();
};
Bar::Bar(char* s) : Foo(s) {...}
BarBar::BarBar : f(“Hello”), x(17) {...}

В конструкторе Bar список инициализации членов используется для инициализации базового класса
Foo. Компилятор выбирает используемый конструктор на основании сигнатуры, определяемой по
фактическим аргументам. При отсутствии списка инициализации членов сконструировать Bar было бы
невозможно, поскольку компилятор не мог бы определить, какое значение должно передаваться
конструктору базового класса Foo. В конструкторе BarBar список инициализации членов
использовался для инициализации (то есть вызова конструкторов) переменных f и х. В следующем
варианте конструктор работает не столь эффективно (если только компилятор не отличается
сверхъестественным интеллектом):
Код

BarBar::BarBar() : f(“Hello”)
{
x = 17;
}


Во втором варианте переменная х сначала инициализируется значением 0 (стандартное требование
C++) с использованием по умолчанию конструктора int без аргументов, а затем в теле конструктора
ей присваивается значение 17. В первом варианте имеется всего одна инициализация и потому
экономится один-два машинных такта. В данном примере это несущественно, поскольку переменная
х — целая, но если бы она относилась к более сложному классу с конструктором без аргументов и
перегруженным оператором присваивания, то разница была бы вполне ощутима.
Списки инициализации членов нужны там, где у базового класса или переменной нет конструктора без
аргументов (точнее, есть один и более конструктор с аргументами, но нет ни одного определенного
пользователем конструктора без аргументов). Списки инициализации членов не обязательны в тех
ситуациях, когда все базовые классы и переменные класса либо не имеют конструкторов, либо имеют
пользовательский конструктор без аргументов.

Когда ты пишешь, например, так:
Код

Counter() 
{
    count = 10;
}

Фактически происходит вот что:
Код

Counter() : count()
{
    count = 10;
}

Автор: Fazil6 27.3.2007, 11:40
Цитата(KelTron @  27.3.2007,  11:14 Найти цитируемый пост)
Фактически происходит вот что:

вообще-то не совсем , count не инициализируется нулем поумолчанию

Автор: Goryachev 27.3.2007, 11:46
Цитата(KelTron @  27.3.2007,  11:14 Найти цитируемый пост)
Когда ты пишешь, например, так:

Код

Counter() 
{
    count = 10;
}


Фактически происходит вот что:

Код

Counter() : count(0)
{
    count = 10;
}



Это не правильно.
Пример на gcc:
Код

#include <iostream>

class A
{
    int x;
public:
    int GetX() { return x; }
};

int main(int argc, char** argv)
{
    A a;
    std::cout<<a.GetX()<<std::endl;
    return 0;
}



Ресультат:
Код

4911516

То есть, мусор.

Автор: seacat79 27.3.2007, 11:55
Цитата(Fazil6 @  27.3.2007,  11:40 Найти цитируемый пост)
вообще-то не совсем , count не инициализируется нулем поумолчанию


А если count это:  smile 
Код

class my_integer
{
    public:
         my_integer(int z=0);
}


Автор: Goryachev 27.3.2007, 12:06
Цитата(seacat79 @  27.3.2007,  11:55 Найти цитируемый пост)
А если count это:   

Код

class my_integer
{
    public:
         my_integer(int z=0);
}


Я думаю, что count подразумевался, как в оригинальном посте, т.е. int. ;)

Автор: KelTron 27.3.2007, 12:19
Исправлюсь, происходит вот что:
Код

Counter() : count()
{
    count = 10;
}

Автор: vinter 27.3.2007, 12:52
Цитата(Goryachev @  27.3.2007,  11:46 Найти цитируемый пост)
Это не правильно.

и где это не правильно?? это инициализация внутри тела конструктора!
Цитата(Goryachev @  27.3.2007,  11:46 Найти цитируемый пост)
Пример на gcc:

и что показывает твой пример? там не переопределен конструктор!
Цитата(KelTron @  27.3.2007,  12:19 Найти цитируемый пост)
Исправлюсь, происходит вот что:

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

Автор: KelTron 27.3.2007, 13:00
Цитата(vinter @  27.3.2007,  12:52 Найти цитируемый пост)
вряд ли, скорее все соптимизировано в создание с инициализацией сразу, все таки встроенный тип.Зачем ему сначала создаваться, а потом инициализироваться

Ну не надо вдаваться в частные случаи, оно может и соптимизироваться, не в этом дело, в общем случае происходит именно то что я написал т.е. вызываются конструкторы по умолчанию для объектов, тип int здесь только потому, что он в первом посту. 

Автор: Fazil6 27.3.2007, 13:04
Цитата(vinter @  27.3.2007,  12:52 Найти цитируемый пост)
и что показывает твой пример? там не переопределен конструктор!

ты не понял. Речь о инициализации поумолчанию и Goryachev, в этом плане прав. 
Цитата(vinter @  27.3.2007,  12:52 Найти цитируемый пост)
вряд ли, скорее все соптимизировано в создание с инициализацией сразу, все таки встроенный тип.Зачем ему сначала создаваться, а потом инициализироваться

создается до вызова конструктора и если нет в списке инициализации, то значение не присваивается. Пока не произойдет присваивание внутри конструктора значение неопределено.

Автор: vinter 27.3.2007, 13:31
Цитата(Fazil6 @  27.3.2007,  13:04 Найти цитируемый пост)
ты не понял. Речь о инициализации поумолчанию и Goryachev, в этом плане прав. 

теперь понял smile
Цитата(Fazil6 @  27.3.2007,  13:04 Найти цитируемый пост)
создается до вызова конструктора и если нет в списке инициализации, то значение не присваивается. Пока не произойдет присваивание внутри конструктора значение неопределено.

я про тожеsmile никакого дефолтного конструктора у встр. типов нет.
а вообще тут хватит постов Daevaorn и Earnest, а то развели тут smile 

Автор: JackYF 27.3.2007, 15:07
Цитата(vinter @  27.3.2007,  13:31 Найти цитируемый пост)
Earnest, а то развели тут smile  


оффтоп: гы, у Earnest один пост, у KelTronа  3. Странный у тебя счетчик. Видать, был мусором проинициализирован  smile (это применительно к обсуждаемой теме smile

Без обид smile


Автор: Earnest 27.3.2007, 15:10
Цитата(vinter @  27.3.2007,  14:31 Найти цитируемый пост)
я про тоже никакого дефолтного конструктора у встр. типов нет.

Тоже не совсем правильно. Если написать int a=int(); то получим 0. Правда так в нормальном коде не пишут? но зато это часто встречается в шаблонах. Например, объявляем стандартный вектор интов заданной длины:
Код

   std::vector<int> A(10);

   // это эквивалентно следующей строке
   std::vector<int> A(10, int());    // там аргумент по умолчанию _Tp val = _Tp()
   // так что получим массив, инициализированный нулями.

Это, кстати, очень удобно. Скажем, есть отображение std::map<CString, int> - допустим, мы хотим подсчитать, сколько раз каждая строка встречается в каком-то тексте.
Пишем:
Код

   std::map<CString, int> counts;   
   ...
   CString str = ReadNextString();
   counts[str]++;    .. если такого элемента еще нет, он создастся и инициализируется нулем!

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

Автор: vinter 27.3.2007, 17:02
Цитата(JackYF @  27.3.2007,  15:07 Найти цитируемый пост)
оффтоп: гы, у Earnest один пост, у KelTronа  3. Странный у тебя счетчик. 

я имел ввиду достаточно двух постов, что дальнейшее обсуждение не имеет смыслаsmile

Автор: VFaraon 28.3.2007, 22:04
Спасибо за информацию!!! Просветили юнца   smile 

Автор: NoNo 29.3.2007, 08:24
 KelTron
 Если он читает книгу Р. Лафоре и он в данный момент изучает только тему "объекты и классы", то "наследование" и "указатели" ему ещё стоит пройти...... Это я по поводу твоего примера.
VFaraon 
А тебе посоветую не замарачивайся сейчас на этом...читай книгу дальше.......а когда станешь понимать лудше.... вернёшься к этому вопросу.....так легче понять......

Автор: VFaraon 1.4.2007, 14:45
Цитата

 Если он читает книгу Р. Лафоре и он в данный момент изучает только тему "объекты и классы", то "наследование" и "указатели" ему ещё стоит пройти...... Это я по поводу твоего примера.
VFaraon 
А тебе посоветую не замарачивайся сейчас на этом...читай книгу дальше.......а когда станешь понимать лудше.... вернёшься к этому вопросу.....так легче понять......


Нет нет. Я уже прочитал одну книгу, там есть все, но очень просто написано (т.к. книга для совсем не знакомых с ВЕЛИКИМ  smile  С++). Я ведь купил Лафоре для того, чтоб понять принципы ООП. Но там еще много мелочей неизвесных мне. В общем полезная для меня книга. Я это к тому что ПОЛНОСТЬЮ понял смысл сообщения KelTron!


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)