LINUX.ORG.RU

[c++] Инициализация в теле конструктора или в списке инициализации - большая ли разница в сгенерированном коде?


0

1

Вот интересно, положим есть класс A с примером инициализации указателей

class A
{
private:
  int *A, *B, *C;
public:
  A() : A(NULL), B(NULL), C(NULL)
  {
    A=NULL;
    B=NULL;
    C=NULL;
  }
};
Тут я привёл 2 возможных типа инициализации. Можно либо так, либо так. Если выполняется тело функции, то каждый указатель обнуляется последовательно. Это поведение кажется логичным для обыкновенной функции, возможно оно таким же будет и для конструктора? Если же в классе есть список инициализации, то компилятор может сгенерировать код для забивания памяти нулями какими-нибудь цепочечными командами ещё до вызова конструктора, что в результате будет несколько производительнее. Правильны ли мои соображения?


g++ -S спасёт отца русской демократии

mi_estas ()
Ответ на: комментарий от mi_estas

>g++ -S

Это может немного подсказать, но не позволит сделать окончательные выводы. Меня пока интересует не конкретная реализация, а общие соображения как лучше делать в своём проекте. В g++ может не оказаться ничего подобного или вообще я натолкнусь на регрессию в конкретной версии.

gogi ()
Ответ на: комментарий от gogi

Так а что же по Вашему может подсказать точнее чем асм код ?

Jetty ★★★★★ ()

>Если же в классе есть список инициализации, то компилятор может сгенерировать код для забивания памяти нулями какими-нибудь цепочечными командами ещё до вызова конструктора, что в результате будет несколько производительнее
А почему бы ему не генерить такойже «более производительный» если ты присваиваешь значения не в списке инициализации, а в теле конструктора. Помоему с точки зрения языка, здесь вообще нет разницы, а с точки зрения конкретной реализации компилятора... Вопщем что я хотел сказать: Я не знаю :-)))

Bad_ptr ★★★★ ()

>Тут я привёл 2 возможных типа инициализации. Можно либо так, либо так

Нет. У тебя инициализация и присваивание. Для простых типов разницы не будет. Для сложных объектов вариант с присваиванием может вызвать прочедание по производительности.

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

legolegs ★★★★★ ()
Ответ на: комментарий от Jetty

>А вдруг у него 5 тыщ переменных :)

Ну вообще-то да. Я имею ввиду оптимизацию для всей программы, в которой могут быть сотни класов/структур, в каждом из которых по несколько десятков полей.

gogi ()
Ответ на: комментарий от legolegs

>Для сложных объектов вариант с присваиванием может вызвать прочедание по производительности.

Компилятор сгенерирует вызов конструктора для того сложного объекта с другими параметрами или что ты имеешь ввиду?

gogi ()
Ответ на: комментарий от Bad_ptr

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

В списке есть только присвоение и/или вызов функций. В теле функции может быть почти какой угодно код. Инициализация через оператор присвоения может быть обвёрнута условной компиляцией, условными операторами, вообще размещаться в других функциях вызываемых из конструтора (какой угодно вложенности) и т.д. То есть такой код гораздо сложнее анализировать в общем случае.

gogi ()
Ответ на: комментарий от Jetty

>Так а что же по Вашему может подсказать точнее чем асм код ?

Например стандарт или документация на компилятор. Но мне по обсуждаемому вопросу ничего не известно.

gogi ()
Ответ на: комментарий от reader

>http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6

Для сложных типов стало примерно ясно.
Далее, там есть вот такое:

Note: There is no performance difference if the type of x_ is some built-in/intrinsic type, such as int or char* or float


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

gogi ()
Ответ на: комментарий от gogi

можно получить небольшой прирост на больших классах с хорошим компилятором

а нужно ли? часто ли тебе попадаются системы, в которых инициализация POD'ов является боттлнеком?

jtootf ★★★★★ ()

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

s0L ()

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

s0L ()

Ещё есть следующее отличие:

в списке инициализации члены будут инициализированы в том порядке, в котором они были объявлены. Например

class A {
  int _b;
  int _a;
public:
  A(int a, int b): _a(a), _b(b) {}
};

Здесь первым будет проиницилизирован _b.

А в теле конструктора можешь сам выбирать порядок. Правда я сходу не могу привести примера, когда это может быть существенно =)

DELIRIUM ★★★★★ ()

а почитать «кресты для чайников»?

истинным является утверждение: списки инициализации не медленнее.

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

anonymous ()
Ответ на: комментарий от anonymous

Я конечно не спец по оптимизации но разве не верно то утверждение что списки инициализации стоит применять тогда, когда инициализировать объект невозможно в теле функции. Я о константных переменных и классах-родителях( может там еще что то есть, я сейчас не помню ). По моей логике при написании больших программ для всех элемент-данных класса должны быть сделаны set и get функции, и инициализация простых переменных должна делаться через сеттеры, причем только тех, которые действительно нуждаются в присвоении значения. Если инициализация каких либо переменных на раннем этапе не нужна, то зачем их собственно говоря инициализировать...А вот константные данные или инициализация классов - родителей должны происходить по спискам инициализации, дабы другого варианта нет. В итоге производительность упадет только при вызове сеттеров, но там где это действительно необходимо, остальные переменные будут заполнены мусором до их первого использования. При написании мелких программ можно не заморачиваться реализацией сеттеров и геттеров и присвоить значения в лоб.

energyclab ()
Ответ на: комментарий от DELIRIUM

>в списке инициализации члены будут инициализированы в том порядке, в котором они были объявлены ... А в теле конструктора можешь сам выбирать порядок.

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

s0L ()
Ответ на: комментарий от s0L

Согласен.

#include <iostream>

using std::cout;
class A {
public:
	A() { cout << "A()\n"; }
	A(int i) { cout << "A(int)\n"; }
};

class B {
	A a;
public:
	B(int i) { a = i; }
};

int main()
{
	B b(0);
}

[del@del-arch ~]$ g++ test.cpp -o init_test
[del@del-arch ~]$ ./init_test 
A()
A(int)

DELIRIUM ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.