В этом конкретном случае существует ли разница между использованием списка инициализаторов членов и назначением значений в конструкторе?

Внутренне и о сгенерированном коде существует ли действительно разница между:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

и

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

спасибо...

Ответ 1

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

Ответ 2

Вам нужно использовать список инициализации для инициализации постоянных членов, ссылок и базового класса

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

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

Пример (не исчерпывающий) class/struct содержит ссылку:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

И пример инициализации базового класса, для которого требуется параметр (например, конструктор по умолчанию):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

Ответ 3

Да. В первом случае вы можете объявить _capacity, _data и _len как константы:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

Это было бы важно, если вы хотите обеспечить const -ness этих переменных экземпляра при вычислении их значений во время выполнения, например:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

const экземпляры должны быть инициализированы в списке инициализаторов, или базовые типы должны предоставлять общедоступные конструкторы без параметров (какие примитивные типы делают).

Ответ 4

Я думаю, что эта ссылка http://www.cplusplus.com/forum/articles/17820/ дает отличное объяснение - особенно для тех, кто не знаком с С++.

Причина, по которой списки intialiser более эффективны, заключается в том, что внутри тела конструктора выполняются только назначения, а не инициализация. Поэтому, если вы имеете дело с нестрочным типом, конструктор по умолчанию для этого объекта уже вызывается до того, как был введен тело конструктора. Внутри тела конструктора вы присваиваете значение этому объекту.

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

Ответ 5

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

Ответ 6

Большая разница заключается в том, что назначение может инициализировать элементы родительского класса; инициализатор работает только с членами, объявленными в текущем классе.

Ответ 7

Зависит от соответствующих типов. Разница аналогична между

std::string a;
a = "hai";

и

std::string a("hai");

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

Ответ 8

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

  • (phase1) Перед телом init (включая список инициализации): компилятор выделяет требуемую память для класса. Класс уже жив!
  • (phase2) В элементе init: поскольку выделена память, каждое присвоение теперь указывает на операцию с уже выходящей/инициализированной переменной.

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

  • Элементы типа const должны быть инициализированы перед телом init.
  • После фазы 1 любая операция записи действительна только для непостоянных членов.

Ответ 9

Существует только один способ инициализировать экземпляры базового класса и нестатические переменные-члены и использовать список инициализаторов.

Если вы не укажете базовую или нестационарную переменную-член в списке инициализатора конструктора, то этот элемент или база будут либо инициализированы по умолчанию (если член/база является классом класса не-POD или массивом не- -POD) или оставлено неинициализированным в противном случае.

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

Возможно, вы сможете назначать новые значения членам в теле конструктора, но невозможно назначить членам const или членам типа класса, которые были сделаны несвязаемыми, и невозможно перепроверить ссылки.

Для встроенных типов и некоторых пользовательских типов назначение в теле конструктора может иметь тот же эффект, что и инициализация с тем же значением в списке инициализаторов.

Если вы не можете назвать член или базу в списке инициализаторов, и этот объект является ссылкой, имеет тип класса без доступного объявленного пользователем конструктора по умолчанию, const квалифицирован и имеет тип POD или тип класса POD или массив класса класса POD, содержащий const квалифицированный член (прямо или косвенно), тогда программа плохо сформирована.

Ответ 10

Если вы пишете список инициализаторов, вы делаете все за один шаг; если вы не пишете список инициализаторов, вы сделаете 2 шага: один для объявления и один для присвоения значения.

Ответ 11

В конструкторе есть разница между списком инициализации и инициализацией. Рассмотрим ниже код:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

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

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

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

Ответ 12

Вот момент, когда я не видел, чтобы другие ссылались на него:

class temp{
public:
   temp(int var);
};

У временного класса нет ctor по умолчанию. Когда мы используем его в другом классе следующим образом:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

код не будет компилироваться, потому что компилятор не знает, как инициализировать obj, потому что у него есть только явный ctor, который получает значение int, поэтому мы должны изменить ctor следующим образом:

mainClass(int sth):obj(sth){}

Итак, речь идет не только о const и ссылках!