Всё глубже и глубже погружаясь в C++, я начинаю немного сходить с ума, виной этому то, что некоторые вещи я просто не могу объяснить, а заучивать отдельные случаи как-то глупо. Сейчас на моём пути конструктор копий... Сразу оговорюсь, что я использую C++ 17. Так вот, возьмём этот код
class A {
public:
A() {
cout << "construct\n";
}
~A() {
cout << "destruct\n";
}
A(const A &obj) {
cout << "copy\n";
}
};
A f() {
A a;
cout << &a << "\n";
return a;
}
int main() {
A a(f());
cout << &a << "\n";
}
Результат его выполнения
construct
0x7ffdca371107
0x7ffdca371107
destruct
Честно говоря я без понятия почему результат такой. По идее при возвращении из функции должна создаваться копия объекта, затем при инициализации переменной в main'e
должен вызываться конструктор копии этой копии объекта, в результате должны получить новый объект с новым адресом, но бит в бит такой же как его копия. Но в результате всё не так, при этом абсолютно не так. А теперь ещё для кого-то сюрприз, для кого-то нет: закомментируем обязательно и деструктор, и конструктор копии, если что-то из них останется, то это не прокатит. Теперь результат такой
construct
0x7fff090c99f7
0x7fff090c9a27
А вот это уже больше походит на то, что я написал ранее. Но всё же я не уверен, что полностью, т.к. я не могу использовать конструктор копии и деструктор, чтобы убедиться, не изменяя поведение кода. Конкретно меня интересует 2 раза ли выполнится код деструктора при инициализации переменной a
в main'e
. По идее 1 раз должен вызваться при завершении функции f()
, а 2 после того, как инициализируется переменная a
. Меня интересуют ответы на поставленные вопросы, а также логика, почему всё-таки всё работает так, а не иначе.
В данном случае имеют место две оптимизации:
a
, чья область видимости тут же оканчивается, компилятор ограничивается созданием только одной переменной - возвращаемым значением. Эта оптимизация не гарантирована, хотя почти всегда выполняется.f
, материализуется сразу в переменную а
без создания временного объекта. В С++17 компилятор обязан откладывать материализацию временных переменных как можно дальше, устраняя все избыточные промежуточные объекты. Так что цепочки вида A a{A{A{A{A{}}}}};
приводят к появлению только одного объекта, а не целой пачки, и не содержат вызовов копирующих или перемещающих конструкторов (которых может вообще не быть).Таким образом, в функции main
выделяется место только под один объект, который инициализируется в функции f
минуя все промежуточные временные объекты. Причем компилятор пропускает вызовы конструкторов и деструкторов с побочными эффектами.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Питаюсь вводить текст с клавиатуры и сразу отправляю в консоль, с английским без проблем но вместо русского вводить какие то цифры и английские...
Возникло несколько вопросов связанных с raw pointers в С++Рассмотрим следующий код