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


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

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

и

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

спасибо...

11 84

11 ответов:

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

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

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

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
};

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

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) {}
};

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

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

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

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

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

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

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

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

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

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

зависит от используемых типов. Разница между

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

и

std::string a("hai");

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

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

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

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

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

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

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

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

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

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

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

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

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

#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, все члены не инициализируются при выполнении первого оператора в конструкторе.

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