Внутрішньо та щодо сформованого коду, чи справді існує різниця між:
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
-ness цих змінних примірника під час обчислення їх значень під час виконання, наприклад:
MyClass::MyClass() :
_capacity(someMethod()),
_data(someOtherMethod()),
_len(yetAnotherMethod())
{
}
const
екземпляри повинні бути ініціалізовані у списку ініціалізаторів, або базові типи повинні забезпечувати загальнодоступні конструктори без параметрів (що роблять примітивні типи).
Я думаю, що це посилання http://www.cplusplus.com/forum/articles/17820/ дає чудове пояснення - особливо для тих, хто знайомий із C ++.
Причина того, чому списки ініціалізаторів є більш ефективними, полягає в тому, що в тілі конструктора відбуваються лише призначення, а не ініціалізація. Отже, якщо ви маєте справу з невбудованим типом, конструктор за замовчуванням для цього об’єкта вже був викликаний до того, як було введено тіло конструктора. Всередині тіла конструктора ви присвоюєте значення цьому об’єкту.
Фактично це виклик конструктора за замовчуванням, за яким слідує виклик оператору присвоєння копії. Список ініціалізатора дозволяє вам викликати конструктор копіювання безпосередньо, і це іноді може бути значно швидшим (нагадаємо, що список ініціалізатора знаходиться перед тілом конструктора)
Додам, що якщо у вас є члени класу типу без конструктора за замовчуванням, ініціалізація - це єдиний спосіб побудови вашого класу.
Велика різниця полягає в тому, що призначення може ініціалізувати членів батьківського класу; ініціалізатор працює лише з членами, оголошеними в поточній області дії класу.
Справжня різниця зводиться до того, як компілятор gcc генерує машинний код і розміщує пам'ять. Поясніть:
Звичайно, існують інші способи обробки членів типу const. Але щоб полегшити собі життя, автори компіляторів 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){}
Отже, мова йде не лише про const та посилання!