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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Инициализатор элементов, Зачем они нужны вообще? 
:(
    Опции темы
hoz
Дата 27.4.2014, 12:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Часто встречаю такие приёмы, когда переменные инициализируются посредством инициализатора элементов. Хочется понять это просто альтернатива, которая не является необходимой вообще или всё-таки бывает необходима? Например:
Код

class Test
{
public:
   Text (int = 0);
   void print() const;
private:
   int x;
}

Test::Test (int value)
   : x (value)
{
//пустое тело
}
//дальше..
//объявляем элементы..
//..класса

переменной x же можно было присвоить значение value внутри конструктора, зачем было объявлять его через : после заголовка?

Это сообщение отредактировал(а) hoz - 27.4.2014, 13:49
PM MAIL   Вверх
BlackSpace
Дата 27.4.2014, 18:05 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



hoz, да это альтернативный способ, но он боле эффективен для некоторых типов данных, ну а в некоторых случаях без него не обойтись.
В C++ - при создании объекта все члены данных объекта должны быть созданы до вызова конструктора. 
Список инициализации позволяет задать начальные данные объекту в момент его создания.

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

Плюс еще для const члена данных не прокатит присваивание. Только инициализация при создании. Также ссылочный тип данных.
Вот пример с const.

Код

#include <iostream>

using namespace std;

class A {
public:
    const int a;

    A() : a( 6 ) {
    }
    
    /*
     A() {
     a = 6; // не прокатит
     }
     */
};

int main() {
    A obj;
    cout << obj.a << endl;
    return 0;
}


Это сообщение отредактировал(а) BlackSpace - 27.4.2014, 18:19
PM MAIL   Вверх
hoz
Дата 27.4.2014, 22:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



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

Цитата(BlackSpace @  27.4.2014,  18:05 Найти цитируемый пост)
В C++ - при создании объекта все члены данных объекта должны быть созданы до вызова конструктора. Список инициализации позволяет задать начальные данные объекту в момент его создания.

А вот тут не совсем понятно. Тут имеется ввиду данные других классов? Т.е. данные, которые не объявлены в теле класса?
PM MAIL   Вверх
BlackSpace
Дата 27.4.2014, 23:35 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



hoz, попробую пояснить на примерах.

Пример 1.
Код

#include <iostream>

using namespace std;

class A {
public:
    string str;

    A() : str( "123" ){

    }

};

int main() {

    A obj;
    cout << obj.str << endl;

    return 0;
}


Пример 2
Код

#include <iostream>

using namespace std;

class A {
public:
    string str;

    A() {
        str = "123";
    }

};

int main() {

    A obj;
    cout << obj.str << endl;

    return 0;
}



1) Все члены данных объекта должны быть созданы до вызова конструктора.
2) Если у объекта класса N есть член данных, который является тоже объектом некого класса M, то до вызова конструктора класса N создается объект класса M
 - Но как? С помощью своего конструктора. 
 - А какие значения членам данных объекта класса M присваиваются?  Такие, которые заложены в его соответствующем конструкторе. 
3) Так вот список инициализации позволяет в момент создания объекта класса N - инициализировать его члены данных. Т.е. инициализировать в данном случае член класса M.

По примерам выше поясню разницу.
str является объектом класса string.
В примере 1 член данных str инициализируется строкой 123 сразу в момент создания объекта класса A.
В примере 2 член данных str инициализируется сначала своим конструктором по умолчанию, только потом происходит модификация строки, когда в теле конструктора присваивание происходит.

Чувствуете отличие? В первом примере мы не вызывали конструктор по умолчанию для члена данных str, а использовали конструктор класса string с одним аргументом.
Вот тут про эффективность думаю понять можно.

Цитата

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


Ничего подобного. Ничего в данном случае не обходится. В классе есть константный член и он не меняется. Его надо как-то инициализировать же при создании объекта класса. Вот в списке инициализации и делается это.
PM MAIL   Вверх
borisbn
Дата 28.4.2014, 09:31 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Позволю себе дополнить пример BlackSpace
Код
struct Tracer {
   Tracer(void)
      :m_name("(none)")
   {
      std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
   }
   Tracer(const std::string & name)
      :m_name(name)
   {
      std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
   }
   Tracer(const Tracer & other)
      :m_name("from " + other.m_name)
   {
      std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
   }
   Tracer & operator=(const Tracer & other) {
      m_name = "from " + other.m_name;
      std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
      return *this;
   }
   ~Tracer() {
      std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
   }
 
   std::string m_name;
};
 
struct A {
    A() : a( "init. list" ) {}
    Tracer a;
};
 
struct B {
    B() {
        b = Tracer( "c-tor" );
    }
    Tracer b;
};
 
int main() {
    {
        A x;
    }
    cout << "1" << endl;
    {
        B x;
    }
    cout << "2" << endl;
}


Вывод:
Цитата
[init. list]    Tracer::Tracer(const string&)
[init. list]    Tracer::~Tracer()
1
[(none)]    Tracer::Tracer()
[c-tor]    Tracer::Tracer(const string&)
[from c-tor]    Tracer& Tracer::operator=(const Tracer&)
[c-tor]    Tracer::~Tracer()
[from c-tor]    Tracer::~Tracer()
2


Вот - http://ideone.com/X4AvnZ - можешь "поиграться"


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
hoz
Дата 1.5.2014, 19:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Цитата(BlackSpace @  27.4.2014,  23:35 Найти цитируемый пост)
2) Если у объекта класса N есть член данных, который является тоже объектом некого класса M, то до вызова конструктора класса N создается объект класса M. 
 - Но как? С помощью своего конструктора

Согласен. Изначально отрабатывают конструкторы объектов, которые являются членами данного класса (В нашем случает объекта класса N). И, отрабатывают они, как я понимаю, в любом случае.

Цитата(BlackSpace @  27.4.2014,  23:35 Найти цитируемый пост)
 - А какие значения членам данных объекта класса M присваиваются?  Такие, которые заложены в его соответствующем конструкторе. 

Это понятно.

Цитата(BlackSpace @  27.4.2014,  23:35 Найти цитируемый пост)
В примере 1 член данных str инициализируется строкой 123 сразу в момент создания объекта класса A.

А как же собственный конструктор класса string ? Судя по ответу, он типа не участвует в инициализации данных класса... А вместо него инициализация происходит напрямую в момент создания объекта класса А.

Цитата(BlackSpace @  27.4.2014,  23:35 Найти цитируемый пост)
В примере 2 член данных str инициализируется сначала своим конструктором по умолчанию, только потом происходит модификация строки, когда в теле конструктора присваивание происходит.

А вот тут я недопонимаю. Сразу не писал, пытался вникунуть, но.. Почему-то тут член данных str инициализируется изначально своим конструктором по умолчанию, а потом уже в теле конструктора.
Удивляет меня то, что согласно теории, в любом учебнике сказано, что при создании объекта конструктор вызывается автоматически. Поэтому странно слышать, что в первом случае минуя автоматический конструктор по умолчанию член данных инициализируется сразу заданным значение. Это как понимать?

Цитата(BlackSpace @  27.4.2014,  23:35 Найти цитируемый пост)
Чувствуете отличие? В первом примере мы не вызывали конструктор по умолчанию для члена данных str, а использовали конструктор класса string с одним аргументом.

Как выше я написал, не стыковка выходит. Если согласно документации конструктор вызывается автоматически, и, всегда, то.. как его можно не вызывать? smile 

Это сообщение отредактировал(а) hoz - 1.5.2014, 19:12
PM MAIL   Вверх
feodorv
Дата 1.5.2014, 21:09 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(hoz @  1.5.2014,  20:11 Найти цитируемый пост)
А как же собственный конструктор класса string ? 

hoz, конструкторов у одного и того же класса может быть несколько. Как с различающимися параметрами, так и без параметров (или с параметрами по умолчанию), последний случай и есть конструктор по умолчанию. Если пользователь явно указал вызов конструктора для какого-либо члена класса, то для него конструктор по умолчанию не вызывается (да и зачем, если и так был вызван другой конструктор с параметрами).
Цитата(BlackSpace @  28.4.2014,  00:35 Найти цитируемый пост)
class A {
public:
    string str;
    A() 
//  вот здесь для str вызывается конструктор по умолчанию
    {
        str = "123";
    }
};


Цитата(BlackSpace @  28.4.2014,  00:35 Найти цитируемый пост)
class A {
public:
    string str;
    A() : str( "123" )
//  а вот здесь для str не вызывается конструктор по умолчанию (str уже инициализирована)
    {
    }
};




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


Шустрый
*


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

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



Цитата(feodorv @  1.5.2014,  21:09 Найти цитируемый пост)
hoz, конструкторов у одного и того же класса может быть несколько. Как с различающимися параметрами, так и без параметров (или с параметрами по умолчанию), последний случай и есть конструктор по умолчанию. 

Но вызывается то один конструктор как ни крути. Хотя их может быть несколько..

Цитата(feodorv @  1.5.2014,  21:09 Найти цитируемый пост)
Если пользователь явно указал вызов конструктора для какого-либо члена класса, то для него конструктор по умолчанию не вызывается (да и зачем, если и так был вызван другой конструктор с параметрами).

А для остальных вызывается? Имею ввиду если членов класса, скажем так, - 10. Явно инициалируется 1 член. Тогда 9 оставшихся членов-данных инициализируются конструктором по умолчанию, верно?

Это сообщение отредактировал(а) hoz - 2.5.2014, 00:17
PM MAIL   Вверх
BlackSpace
Дата 2.5.2014, 04:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



hoz,  smile 

В моем примере 1 для члена данных str вызывается один конструктор ( с одним параметром в данном случае ). И все. str сразу имеет нужный нам вид - "123".
В примере 2 для того же str вызывается сначала конструктор по умолчанию, а лишь потом идет работа по модификации str.

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

Цитата

А для остальных вызывается? Имею ввиду если членов класса, скажем так, - 10. Явно инициалируется 1 член. Тогда 9 оставшихся членов-данных инициализируются конструктором по умолчанию, верно?


Так, приведу еще пример. Правда, 10 классов для примера я не стал писать. Думаю, что для примера сойдет 3 класса, которые будут типами полей в большем классе.
Три ( одинаковых для простоты примера ) класса AB и C. Все имеют конструктор по умолчанию и конструктор с одним параметром. Используемый конструктор можно отследить на стандартном выводе. 
Два ( почти одинаковых ) класса CommonFirst и CommonSecond, которые содержат поля типов AB и C. Разница между двумя классами - в конструкторе с тремя параметрами.

Код
#include <iostream>

using namespace std;

class A {
public:
    int value;

    // конструктор по умолчанию
    A() :
            value( 0 ) {
        cout << "Конструктор по умолчанию класса A" << endl;
    }

    // конструктор с одним параметром
    A( int val ) :
            value( val ) {
        cout << "Конструктор по с одним параметром класса A" << endl;
    }
};

class B {
public:
    int value;

    // конструктор по умолчанию
    B() :
            value( 0 ) {
        cout << "Конструктор по умолчанию класса B" << endl;
    }

    // конструктор с одним параметром
    B( int val ) :
            value( val ) {
        cout << "Конструктор по с одним параметром класса B" << endl;
    }
};

class C {
public:
    int value;

    // конструктор по умолчанию
    C() :
            value( 0 ) {
        cout << "Конструктор по умолчанию класса C" << endl;
    }

    // конструктор с одним параметром
    C( int val ) :
            value( val ) {
        cout << "Конструктор по с одним параметром класса C" << endl;
    }
};


// мы хотим, чтобы при создании объекта класса CommonFirst или класса CommonSecond
// с использованием конструктора с тремя параметрами - поля имели нужные нам значения.


class CommonFirst {
public:
    // поля класса имеют типы данных - другие классы
    A a;
    B b;
    C c;

    // конструктор с тремя параметрами
    // все поля, которым нужны определенные значения при создании объекта - добавляем в список инициализации
    CommonFirst( int _a, int _b, int _c ) :
            a( _a ), b( _b ), c( _c ) {
        // все три поля инициализированы нужными значениями при создании объекта класса CommonFirst
        // произошло это ДО фигурной скобки выше.
        cout << a.value << endl;
        cout << b.value << endl;
        cout << c.value << endl;
    }
};

class CommonSecond {
public:
    // поля класса имеют типы данных - другие классы
    A a;
    B b;
    C c;

    // конструктор с тремя параметрами
    // ТОЛЬКО одно поле из всех, которым нужны определенные значения при создании объекта - добавляем в список инициализации
    // остальные модифицируем ПОСЛЕ создания - уже в теле конструктора
    CommonSecond( int _a, int _b, int _c ) :
            a( _a ) {
        // для двух полей b и c будет вызван СНАЧАЛА конструктор по умолчанию.
        // произошло это ДО фигурной скобки выше.
        cout << a.value << endl; // инициализирован нужным значением при создании объекта класса CommonSecond
        cout << b.value << endl; // ноль
        cout << c.value << endl; // ноль
        // лишь потом будет модификация двух полей b и c.
        b.value = _b;
        c.value = _c;
    }
};

int main() {
    cout << "Создаем объект класса CommonFirst" << endl;
    CommonFirst first( 1, 2, 3 );

    cout << "Создаем объект класса CommonSecond" << endl;
    CommonSecond second( 1, 2, 3 );

    return 0;
}


Вывод программы
Код

Создаем объект класса CommonFirst
Конструктор по с одним параметром класса A
Конструктор по с одним параметром класса B
Конструктор по с одним параметром класса C
1
2
3
Создаем объект класса CommonSecond
Конструктор по с одним параметром класса A
Конструктор по умолчанию класса B
Конструктор по умолчанию класса C
1
0
0

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


Explorer
****


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

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



Цитата(hoz @  2.5.2014,  00:45 Найти цитируемый пост)
Но вызывается то один конструктор как ни крути. Хотя их может быть несколько..

нет, в С++ есть делегирующие конструкторы, которые позволяют сделать цепочку вызова конструкторов сколь угодно длинной.

Цитата(hoz @  2.5.2014,  00:45 Найти цитируемый пост)
А для остальных вызывается? Имею ввиду если членов класса, скажем так, - 10. Явно инициалируется 1 член. Тогда 9 оставшихся членов-данных инициализируются конструктором по умолчанию, верно?

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


--------------------
Мой блог
PM MAIL WWW   Вверх
xvr
Дата 5.5.2014, 16:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

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



Цитата(vinter @  2.5.2014,  17:22 Найти цитируемый пост)
в С++ есть делегирующие конструкторы

Точнее появились в С++11 (так к слову  smile )

PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

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

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

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

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


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

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


 




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


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

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