Порядок вызова конструктора C ++ - PullRequest
3 голосов
/ 22 октября 2011

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

class A
{
public:
   A (string s) { str = string (s); cout << "default" << endl; }
   A (int n) { cout << "A (int n)" << endl; }
   A (string s, int n) { cout << "A (string s, int n)" << endl; }
   A (int n2, string s2) { cout << "A (int n2, string s2)" << endl; }
   A (const A& a) { str = a.str; cout << "copy" << endl; }

   inline void printA () { cout << str << endl; }

   string str;
};


int main (void)
{
   A a_1 = A ("con 1");
   cout << endl;

   A a_2 = "con 2";
   cout << endl;

   A a_3 = A (4);

   A a_4 = A ("a_4", 10);
   cout << endl;

   A a_5 = A (11, "a_5");
   cout << endl;

   cin.get();
   return 0;
}

Результат:

default
copy

default

A (int n)

A (string s, int n)
copy

A (int n2, string s2)
copy

Почему a_1, a_3, а a_4 вызывать как конструкторы по умолчанию, так и конструкторы копирования?A_3 также имеет единственный аргумент, но ему не нужен конструктор копирования.

Ответы [ 4 ]

6 голосов
/ 22 октября 2011

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

A a_1("con 1");
A a_2("con 2");
A a_3(4);
A a_4("a_4", 10);
A a_5(11, "a_5");
5 голосов
/ 22 октября 2011

A a_1 = A ("con 1");

Создает временный объект, вызывая конструктор, который принимает строку в качестве аргумента, поскольку переданный тип равен const char *, компилятор должен сначала выполнить неявное преобразование в string (), а затем использовать этот временный объект для копирования. построить новый a_1 объект.
Поскольку существует дополнительное неявное преобразование, компилятор не может оптимизировать это и должен сделать вызов конструктора копирования.
Основываясь на комментариях и дальнейших исследованиях, я сомневаюсь, что рассуждения (выше , выделенные курсивом ) верны.

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

Однако ни @David, ни Me не могут подтвердить это с помощью стандартной цитаты.

A a_2 = "con 2";

создает объект a_2, вызывая соответствующий конструктор, который принимает строку в качестве аргумента.

A a_3 = A (4);

Примечание в случае 1 также применяется здесь:
В идеале следует сконструировать временный объект, вызвав конструктор, который принимает целое число в качестве аргумента, а затем использовать этот временный объект для копирования, создать новый объект a_3, как в случае 1, но компилятор может оптимизировать и напрямую сконструировать объект, вызвав конструктор, который принимает целое число как доступное

A a_4 = A ("a_4", 10);

Создает временный объект, вызывая конструктор, который принимает строку и целое число в качестве аргумента, а затем использует этот временный объект для копирования, создает новый объект a_4.

A a_5 = A (11, "a_5");

Создает временный объект, вызывая конструктор, который принимает целое число и строку в качестве аргумента, а затем использует этот временный объект для копирования, создает новый объект a_5.

Обратите внимание, что для вашего класса не определен конструктор Default.

Вы можете добиться того же более эффективным способом, избегая создания временного объекта и затем копируя конструирование объекта в вышеупомянутых случаях, не используя присваивание (=).

A a_1("con 1");
A a_2("con 2");
A a_3(4);
A a_4("a_4", 10);
A a_5(11, "a_5");

Мой первоначальный ответ был при попытке объяснить поведение, но когда я скомпилировал это на gcc-4.3.4 на Ideone , я обнаружил, что gcc достаточно умен, чтобы оптимизировать копию вызов конструктора. Ни в одном из случаев не вызывается конструктор копирования.

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

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

0 голосов
/ 22 октября 2011

Краткий ответ

Никто здесь не знает. Вам следует обратиться к автору компилятора.

Или получить другой компилятор.

Нет фундаментальных причин для такого противоречивого поведения.

Подробный ответ

1) показанное поведение компилятора несовместимо

2) только автор компилятора мог ответить на этот вопрос, почему поведение компилятора несовместимо - и, возможно, даже не он, вам нужно будет просмотреть исходный код

3) поэтому лучший не ответ - не тратить слишком много времени на попытки выяснить причину несоответствия поведения компилятора

4) если оптимизация важна для вас, переключите компилятор

0 голосов
/ 22 октября 2011

Причина в том, что вы делаете неявные преобразования. Когда вы создаете a_1, вы используете const char*, который неявно преобразуется в std::string, который подается в конструктор A, а затем копируется в конструкцию. Когда вы создаете a_3, не требуется никакого неявного преобразования, поэтому компилятору разрешается пропускать конструктор копирования и создавать a_3 напрямую с помощью int.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...