Есть ли разница между внутренне и сгенерированным кодом:
MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}
а также
MyClass::MyClass()
{
_capacity=15;
_data=NULL;
_len=0
}
Благодарность...
Ответы:
Если предположить, что эти значения являются примитивными типами, то нет никакой разницы. Списки инициализации имеют значение только тогда, когда у вас есть объекты в качестве членов, поскольку вместо использования инициализации по умолчанию с последующим назначением список инициализации позволяет вам инициализировать объект до его конечного значения. На самом деле это может быть заметно быстрее.
Вам необходимо использовать список инициализации для инициализации постоянных членов, ссылок и базового класса.
Когда вам нужно инициализировать постоянный член, ссылки и передать параметры конструкторам базового класса, как упоминалось в комментариях, вам необходимо использовать список инициализации.
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
есть типы классов без доступных конструкторов по умолчанию?
const
член в теле конструктора, вы должны использовать список инициализации - не const
члены могут быть инициализированы в списке инициализации или в теле конструктора.
Да. В первом случае вы можете объявить _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 ++.
Причина, по которой списки инициализаторов более эффективны, заключается в том, что в теле конструктора выполняются только присваивания, а не инициализация. Поэтому, если вы имеете дело с не встроенным типом, конструктор по умолчанию для этого объекта уже был вызван до того, как было введено тело конструктора. Внутри тела конструктора вы присваиваете значение этому объекту.
По сути, это вызов конструктора по умолчанию, за которым следует вызов оператора присваивания копии. Список инициализаторов позволяет напрямую вызывать конструктор копирования, и иногда это может быть значительно быстрее (напомним, что список инициализаторов находится перед телом конструктора)
Я добавлю, что если у вас есть члены типа класса, у которых нет конструктора по умолчанию, инициализация - единственный способ построить ваш класс.
Большая разница в том, что назначение может инициализировать членов родительского класса; инициализатор работает только с членами, объявленными в текущей области действия класса.
Настоящая разница заключается в том, как компилятор gcc генерирует машинный код и размещает память. Объясните:
Конечно, есть и другие способы обработки членов константного типа. Но чтобы облегчить себе жизнь, авторы компилятора gcc решили установить некоторые правила.
Есть только один способ инициализировать экземпляры базового класса и нестатические переменные-члены - использовать список инициализаторов.
Если вы не укажете базовую или нестатическую переменную-член в списке инициализаторов вашего конструктора, тогда этот член или база будут либо инициализированы по умолчанию (если член / база не является типом класса POD или массивом не-POD класса типы) или оставить неинициализированным в противном случае.
После ввода тела конструктора все базы или члены будут инициализированы или оставлены неинициализированными (т.е. они будут иметь неопределенное значение). В теле конструктора нет возможности влиять на то, как они должны быть инициализированы.
Вы можете назначать новые значения членам в теле конструктора, но невозможно назначить const
членам или членам типа класса, которые были сделаны не назначаемыми, и невозможно повторно связать ссылки.
Для встроенных типов и некоторых определяемых пользователем типов назначение в теле конструктора может иметь тот же эффект, что и инициализация с тем же значением в списке инициализаторов.
Если вы не можете указать член или базу в списке инициализаторов, и эта сущность является ссылкой, имеет тип класса без доступного конструктора по умолчанию, объявленный пользователем, const
квалифицирован и имеет тип POD или является типом класса POD или массивом типа класса POD содержит const
квалифицированного члена (прямо или косвенно), значит, программа плохо сформирована.
Если вы пишете список инициализаторов, вы делаете все за один шаг; если вы не пишете список инициализаторов, вы выполните 2 шага: один для объявления и один для присвоения значения.
Существует разница между списком инициализации и оператором инициализации в конструкторе. Рассмотрим код ниже:
#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, все члены не инициализируются при выполнении первого оператора в конструкторе.
В более позднем случае может возникнуть проблема регрессии, когда кто-то добавил код в конструктор до инициализации определенного члена.
Вот что я не видел, чтобы другие ссылались на это:
class temp{
public:
temp(int var);
};
Класс temp не имеет ctor по умолчанию. Когда мы используем его в другом классе следующим образом:
class mainClass{
public:
mainClass(){}
private:
int a;
temp obj;
};
код не будет компилироваться, потому что компилятор не знает, как инициализировать obj
, потому что у него есть только явный ctor, который получает значение int, поэтому мы должны изменить ctor следующим образом:
mainClass(int sth):obj(sth){}
Итак, дело не только в константах и ссылках!