Конструкторы C ++ и списки инициализации Сравнение скорости

Есть ли какие-либо различия во времени выполнения между конструкторами и списками инициализации? (Или это просто вопрос предпочтения кодирования).
У меня есть набор объектов, которые необходимо создавать часто, и я хотел бы знать, есть ли какой-либо выигрыш в производительности, используя списки инициализации вместо конструкторов.

Если бы мне пришлось создать миллион экземпляров класса A и еще один миллион класса B, какой выбор был бы лучше (объекты представляют пакеты, сгенерированные в сети, отсюда и эти числа).

 class A {
private:
int a, b;

public:
A(int a_var, int b_var):a(a_var), b(b_var) {};
};

class B {
private:
int a, b;

public:
B(int a_var, int b_var) {
a = a_var;
b = b_var;
}
};

Если один из конструкторов будет быстрее, чем другой для примитивных типов (как в примере), будет ли он быстрее, если a и b заменить на типы?

Пример типа:

 class AType {
private:
string a, b;

public:
AType(string a_var, string b_var):a(a_var), b(b_var) {};
};

15

Решение

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

 class B {
private:
SleepyInt a, b;

public:
// takes at least 20s
B(int a_var, int b_var) : a(), b()
//                      ^^^^^^^^^^
{
a = a_var;
b = b_var;
}
};

Если вы не помещаете переменную-член или конструктор базового класса в список инициализации — для них вызывается конструктор по умолчанию. int является базовым типом — его конструктор по умолчанию ничего не стоит — поэтому в вашем примере нет разницы, но для более сложных типов конструктор + присваивание может стоить больше, чем просто конструирование.

Несколько забавный пример, просто чтобы проиллюстрировать разницу:

class SleepyInt {
public:
SleepyInt () {
std::this_thread::sleep_for(std::chrono::milliseconds( 10000 ));
}
SleepyInt (int i) {}
SleepyInt & operator = (int i) { return *this; }
};

class A {
private:
SleepyInt a, b;

public:
A(int a_var, int b_var):a(a_var), b(b_var) {};
};

class B {
private:
SleepyInt a, b;

public:
// takes at least 20s
B(int a_var, int b_var) {
a = a_var;
b = b_var;
}
};
15

Другие решения

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

Списки инициализации можно использовать для инициализации как POD (Plain Old Data), так и пользовательских типов. При инициализации типа POD эффект точно такой же, как у оператора присваивания, то есть нет разницы в производительности между списками инициализации или присваиванием в конструкторе для типов POD.

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

Таким образом, чтобы ответить на вопрос, существует разница в производительности, но только при инициализации не POD-типов.

7

Если члены имеют более или менее сложные типы, то инициализация назначения сначала вызовет вызов конструктора по умолчанию, а затем operator=, что может занять больше времени.

3

Улучшения производительности не будет, если типы будут встроенными / встроенными.

Это говорит:

Заключение: При прочих равных ваш код будет работать быстрее, если
вы используете списки инициализации, а не присваивания.

2

Список инициализации — это полезные ссылочные типы, объекты класса-члена или члены-константы. В противном случае это займет больше времени.

Посмотрите на мой тестовый код:

#include <iostream>
#include <ctime>

using namespace std;

class A{
int a;
public:
A(int a_):a(a_){}
};

class B{
int b;
public:
B(){
}

B(int b_){
b=b_;
}
};

class C{
B b;
public:
C(int c_):b(c_){
}
};

class D{
B b;
public:
D(int d_){
b=d_;
}
};

int main()
{
clock_t start1[10], start2[10], end1[10], end2[10];
for(int j=0;j<10;j++){
start1[j]=clock();
for(int i=0;i<100000;i++){
A *newA=new A(i);
delete newA;
}
end1[j]=clock();
start2[j]=clock();
for(int i=0;i<100000;i++){
B *newB=new B(i);
delete newB;
}
end2[j]=clock();
}
double avg1=0, avg2=0;
for(int i=0;i<10;i++){
avg1+=(end1[i]-start1[i]);
avg2+=(end2[i]-start2[i]);
}
cout << avg1/avg2 << endl;

for(int j=0;j<10;j++){
start1[j]=clock();
for(int i=0;i<100000;i++){
C *newC=new C(i);
delete newC;
}
end1[j]=clock();
start2[j]=clock();
for(int i=0;i<100000;i++){
D *newD=new D(i);
delete newD;
}
end2[j]=clock();
}
avg1=avg2=0;
for(int i=0;i<10;i++){
avg1+=(end1[i]-start1[i]);
avg2+=(end2[i]-start2[i]);
}
cout << avg1/avg2 << endl;

system("pause");
return 0;
}

Пример выводов, как это:

1,02391
0.934741

2
По вопросам рекламы [email protected]