Рубрики

Когда мы используем список инициализаторов в C ++?

Список инициализатора используется при инициализации членов данных класса. Список инициализируемых элементов указывается конструктором в виде списка через запятую, за которым следует двоеточие. Ниже приведен пример, который использует список инициализаторов для инициализации x и y класса Point.

#include<iostream>

using namespace std;

  

class Point {

private:

    int x;

    int y;

public:

    Point(int i = 0, int j = 0):x(i), y(j) {} 

    / * Вышеприведенное использование списка инициализатора необязательно, так как

        Конструктор также может быть записан как:

        Точка (int i = 0, int j = 0) {

            х = я;

            у = j;

        }

    * /    

      

    int getX() const {return x;}

    int getY() const {return y;}

};

  

int main() {

  Point t1(10, 15);

  cout<<"x = "<<t1.getX()<<", ";

  cout<<"y = "<<t1.getY();

  return 0;

}

  
/* ВЫХОД:

   х = 10, у = 15

* /

Приведенный выше код является лишь примером синтаксиса списка инициализатора. В приведенном выше коде x и y также могут быть легко инициализированы внутри конструктора. Но бывают ситуации, когда инициализация элементов данных внутри конструктора не работает, и необходимо использовать список инициализатора. Ниже приведены такие случаи:

1) Для инициализации нестатических элементов данных const:
члены данных const должны быть инициализированы с использованием списка инициализаторов. В следующем примере «t» является постоянным членом данных класса Test и инициализируется с использованием списка инициализаторов. Причина инициализации элемента данных const в списке инициализаторов заключается в том, что для элемента данных const отдельно не выделяется память, она складывается в таблицу символов, из-за чего нам нужно инициализировать ее в списке инициализаторов.
Кроме того, это конструктор копирования, и нам не нужно вызывать оператор присваивания, что означает, что мы избегаем одной дополнительной операции.

#include<iostream>

using namespace std;

  

class Test {

    const int t;

public:

    Test(int t):t(t) {}  // Список инициализатора должен использоваться

    int getT() { return t; }

};

  

int main() {

    Test t1(10);

    cout<<t1.getT();

    return 0;

}

  
/* ВЫХОД:

   10

* /

2) Для инициализации референтных членов:
Ссылочные элементы должны быть инициализированы с использованием списка инициализаторов. В следующем примере «t» является ссылочным членом класса Test и инициализируется с использованием списка инициализаторов.

// Инициализация элементов справочных данных
#include<iostream>

using namespace std;

  

class Test {

    int &t;

public:

    Test(int &t):t(t) {}  // Список инициализатора должен использоваться

    int getT() { return t; }

};

  

int main() {

    int x = 20;

    Test t1(x);

    cout<<t1.getT()<<endl;

    x = 30;

    cout<<t1.getT()<<endl;

    return 0;

}
/* ВЫХОД:

    20

    30

 * /

3) Для инициализации объектов-членов, которые не имеют конструктора по умолчанию:
В следующем примере объект «a» класса «A» является членом данных класса «B», а «A» не имеет конструктора по умолчанию. Список инициализатора должен использоваться для инициализации «а».

#include <iostream>

using namespace std;

  

class A {

    int i;

public:

    A(int );

};

  

A::A(int arg) {

    i = arg;

    cout << "A's Constructor called: Value of i: " << i << endl;

}

  
// Класс B содержит объект A

class B {

    A a;

public:

    B(int );

};

  

B::B(int x):a(x) {  // Список инициализатора должен использоваться

    cout << "B's Constructor called";

}

  

int main() {

    B obj(10);

    return 0;

}
/* ВЫХОД:

    Конструктор А называется: Значение i: 10

    Конструктор Б называется

* /

Если у класса A были как конструкторы по умолчанию, так и параметризованные, то список Initializer List не обязателен, если мы хотим инициализировать «a» с помощью конструктора по умолчанию, но он должен инициализировать «a» с помощью параметризованного конструктора.

4) Для инициализации членов базового класса: Как и в пункте 3, параметризованный конструктор базового класса может быть вызван только с использованием списка инициализатора.

#include <iostream>

using namespace std;

  

class A {

    int i;

public:

    A(int );

};

  

A::A(int arg) {

    i = arg;

    cout << "A's Constructor called: Value of i: " << i << endl;

}

  
// Класс B получен из A

class B: A {

public:

    B(int );

};

  

B::B(int x):A(x) { // Список инициализатора должен использоваться

    cout << "B's Constructor called";

}

  

int main() {

    B obj(10);

    return 0;

}

5) Когда имя параметра конструктора совпадает с элементом данных
Если имя параметра конструктора совпадает с именем элемента данных, то элемент данных должен быть инициализирован с использованием этого указателя или списка инициализаторов. В следующем примере имя члена и имя параметра для A () — «i».

#include <iostream>

using namespace std;

  

class A {

    int i;

public:

    A(int );

    int getI() const { return i; }

};

  

A::A(int i):i(i) { }  // Должен использоваться либо список инициализатора, либо этот указатель

/ * Вышеупомянутый конструктор также может быть записан как
A :: A (int i) {

    это-> я = я;

}
* /

  

int main() {

    A a(10);

    cout<<a.getI();

    return 0;

}
/* ВЫХОД:

    10

* /

6) По причинам производительности:
Лучше инициализировать все переменные класса в Списке инициализаторов, а не присваивать значения внутри тела. Рассмотрим следующий пример:

// без списка инициализаторов

class MyClass {

    Type variable;

public:

    MyClass(Type a) {  // Предположим, что тип уже

                     // объявлен класс и имеет соответствующий

                     // конструкторы и операторы

      variable = a;

    }

};

Здесь компилятор выполняет следующие шаги для создания объекта типа MyClass
1. Конструктор типа сначала вызывается как «а».
2. Оператор присваивания «Type» вызывается внутри тела конструктора MyClass () для назначения

    variable = a; 

3. И, наконец, деструктор типа «Type» вызывается как «a», поскольку он выходит из области видимости.

Теперь рассмотрим тот же код с конструктором MyClass () со списком инициализаторов

// со списком инициализаторов

class MyClass {

    Type variable;

public:

    MyClass(Type a):variable(a) {   // Предположим, что тип уже

                     // объявлен класс и имеет соответствующий

                     // конструкторы и операторы

    }

};

При использовании списка инициализатора компилятор выполняет следующие шаги:
1. Конструктор копирования класса «Тип» вызывается для инициализации: переменная (а). Аргументы в списке инициализатора используются для непосредственного копирования конструкции «переменная».
2. Деструктор «Тип» называется «а», так как он выходит из области видимости.

Как видно из этого примера, если мы используем присваивание внутри тела конструктора, есть три вызова функций: конструктор + деструктор + один дополнительный вызов оператора присваивания. И если мы используем Initializer List, то есть только два вызова функций: копирование конструктора + вызов деструктора. Смотрите этот пост для бегущего примера по этому вопросу.

Этот штраф назначений будет гораздо больше в «реальных» приложениях, где будет много таких переменных. Спасибо ptr за добавление этого пункта.

Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.

Рекомендуемые посты:

Когда мы используем список инициализаторов в C ++?

0.00 (0%) 0 votes