7

У меня есть общий вопрос, который может быть немного специфичным для компилятора. Меня интересуют условия, при которых будет вызываться конструктор. В частности, в режиме режим/сборки, оптимизированные для скорости, всегда будет вызываться созданный компилятором или пустой конструктор при создании экземпляра объекта?Являются ли пустые конструкторы, всегда вызываемые в C++?

class NoConstructor 
{ 
    int member; 
}; 

class EmptyConstructor 
{ 
    int member; 
}; 

class InitConstructor 
{ 
    InitConstructor() 
     : member(3) 
    {} 
    int member; 
}; 

int main(int argc, _TCHAR* argv[]) 
{ 
    NoConstructor* nc = new NoConstructor(); //will this call the generated constructor? 
    EmptyConstructor* ec = new EmptyConstructor(); //will this call the empty constructor? 
    InitConstructor* ic = new InitConstructor(); //this will call the defined constructor 

    EmptyConstructor* ecArray = new EmptyConstructor[100]; //is this any different? 
} 

Я много раз искал и некоторое время просматривал сгенерированный код сборки в Visual Studio. Тем не менее, это может быть сложно выполнить в сборках релизов.

Итого: Является ли конструктор всегда называемым? Если да, то почему?

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

+1

Если вы не возражаете против моего запроса, это просто праздное любопытство или качество вашего готового продукта зависит от того, оптимизирует ли этот компилятор для этого? – Blrfl

+1

Немного обоим. Я пишу класс массива (как упражнение), который использует нечто похожее на boost :: is_pod <>, чтобы решить, следует ли использовать специализированную специализацию с реализацией, использующей memswap/memcopy, вместо явного вызова конструкторов/деструкторов/операторов присваивания. Меня заинтересовало то, насколько сильно это повлияло на производительность для типов контейнеров. – JBeFat

+2

Надеюсь, вы не так динамично выделяете это в своей реальной программе. – GManNickG

ответ

5

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

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

Все это может стать даже фантазией, если ваш компилятор/линкер имеет LTO.

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

Вот разборки для кода в вашем примере (x86_64, GCC 4.4.5):

main: 
    subq $8, %rsp 
    movl $4, %edi 
    call _Znwm 
    movl $4, %edi 
    movl $0, (%rax) 
    call _Znwm 
    movl $4, %edi 
    movl $0, (%rax) 
    call _Znwm 
    movl $400, %edi 
    movl $3, (%rax) 
    call _Znam 
    xorl %eax, %eax 
    addq $8, %rsp 
    ret 

Как вы можете видеть, нет Конструкторы называют вообще. Нет классов вообще, каждый объект - всего 4 байта.

С MS компилятором, YMMV. Поэтому вам нужно проверить разборку самостоятельно. Но результат должен быть аналогичным.

Удачи вам!

+0

Удивительный ответ. Благодаря! Я потратил немало времени на проверку разборки. На самом деле я не был удовлетворен тем, что итоговая сборка (аналогичная тому, что вы указали выше) была доказательством того, что конструктор не будет вызван. Что, если я выделил 100 из этих объектов, из какого-то другого класса? Разумеется, компилятор просто не попытался бы вставить данные в стек. Вот здесь все стало сложно, и я подумал, что попрошу здесь ... :) – JBeFat

+0

@JBeFat: В этом случае компилятор будет выровнять данные (если упаковка не включена) и выделить куски соответствующего размера. Но конструктор все равно не будет вызван. –

10

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

+0

Я подозревал это, но у меня проблемы с проверкой/проверкой. Есть ли у вас какие-либо ссылки на поддержку этого, или, может быть, на какой-то пример disassemly? – JBeFat

+0

Любое доказательство будет зависеть от компилятора и флагов, и не является действительно доказательством, поскольку вы не можете охватить все возможные случаи путем тестирования. Какой компилятор вы используете? – Erik

+0

В настоящее время MSVC9. Любые советы о том, как протестировать это поведение, были бы замечательными! – JBeFat

15

Будет ли вызывать конструктор конструктора/пустой конструктор, когда вы создаете экземпляр объекта?

No. Если ваш класс является так называемый «POD» (простые старые данные), то сгенерированный компилятором конструктор не всегда можно назвать.

В частности, он не будет называться в двух следующих случаях:

struct Pod { 
    int x; 
}; 

int main() { 
    Pod pod; 
    std::cout << pos.x << std::endl; // Value undefined. 

    Pod pod2 = Pod(); // Explicit value initialization. 


    Pod* pods = new Pod[10]; 
    // Values of `pods` undefined. 

    Pod* pods2 = new Pod[10](); // Explicit value initialization. 
} 

условия для того, когда именно тип является POD немного сложнее. C++ FAQ has a nice breakdown.

+0

Спасибо, что указали это. – RageD

+0

Это хороший ответ. Спасибо Konrad :). Мой следующий вопрос будет: если у объекта нет явного (определяемого пользователем) конструктора, почему компилятор не всегда его оптимизирует (если класс не содержит каких-либо объектов-членов, определяющих конструкторы)? Я просто дал неопределенное определение типа стручка? – JBeFat

+0

'Pod * pods2 = new Pod [10](); // Явный вызов конструктора. Этот комментарий неверен, вызовов конструктора нет.'()' вызывает инициализацию значения, которая происходит в начале вызова конструктора. Неявно определенный конструктор по умолчанию оставил член 'x' неинициализированным. Точно так же в 'Pod pod2 = Pod();', в то время как верно, что есть вызов конструктора, вызывается только конструктор копирования. –

1

Определенные типы классов или структур называются POD «Обычные старые данные» на C++. Эти типы не будут имеют конструкторы.

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

0

Ваш образец не очень хорош, вы пропустили ключевое слово public в классах образцов, и, кроме того, в ваших примерах принудительно нулевой инициализации, написав CLASS * class = new CLASS(); вместо CLASS * class = new CLASS;.

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

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

+0

Код был введен в окне браузера. Он может не компилироваться, но он имеет смысл. Вопрос специфичен для компилятора, но похоже, что поведение, которое было бы довольно стандартным для компиляторов. Что вы подразумеваете под нулевой инициализацией? Это не очень полезный ответ, но спасибо, что нашли время ответить :) – JBeFat

+0

как я писал - появилось несколько лучших ответов :) Я имел в виду, что 'new DUMMY' и' new DUMMY() 'различаются вещи. Последняя активирует инициализацию - трюк, который часто используется для инициализации POD с нулевым значением. – Andrey

+0

кстати, хороший вопрос. Мы можем ожидать много дискуссий о том, что кажется или кажется не «вызовом конструктора») – Andrey

Смежные вопросы