Странный bag? в компиляторе С++ VS.NET
Проблема с вызовами конструкторов и деструкторов.
class A
{
public:
int *ptr;
A(int a) {ptr=new int[20]; cout<<"A ";}
~A(){cout<<"~A "; delete[] ptr;}
// A(const A& a) {ptr=new int[20]; cout<<"copy constr";}
};
class B
{
public:
A *a;
B(A a){}
};
B b(A(5));
Сначала создается неименованный объект А(5). Затем делается его точная копия
во временную переменную, которая и передается в конструктор В. На самом деле
вроде бы и имеет право (хотя не совсем уверен Смотрим далее. При выходе
из конструктора В вызывается деструктор временной копии! (а ведь конструктор
ее не вызывался!) Затем, уже после вызова конструктора В вызывается деструктор
для неименованного объекта А(5). На самом деле ничего противозаконного.
Но так как внутри класса А распределяется память двойной вызов деструктора
приводит к ошибочному повторному освобождению ptr. Классика жанра
Делаем, как и полагается в приличных программах, конструктор копии
(закоментарен в пред. примере). И что же присходит? - совсем не то, что
ожидается! Конструктор копии НЕ вызывается, временная переменная для передачи
объекта А(5) в конструктор В НЕ СОЗДАЕТСЯ и деструктор вызывается только
ОДИН РАЗ! Я так и не понял в чем тут хохма.
Кстати сказать, компилятер Borland C++ 5.5 (от Builder) вообще с самого начала
не создавал временной переменной и сразу передавал созданный A(5) в В().
В выражении "B b(A(5));" создается временный безымянный объект типа A. Так как этот объект не имеет имени, вместо него будет использована ссылка(константная). Но, конструктор "B::B(A a)" в качестве параметра принимает объект, а не ссылку на него. Из-за этого будет вызван конструктор копирования для создания объекта, который будет передан конструктору B::B(A a). Если в классе A явно не определен copy ctor, то вместо него будет вызван конструктор копирования, определенный компилятором, который будет почленно копировать объекты(но не будет выделять память, которая удаляется в деструкторе). Хорошо, определим copy ctor...
И что же присходит? - совсем не то, что
ожидается! Конструктор копии НЕ вызывается, временная переменная для передачи
объекта А(5) в конструктор В НЕ СОЗДАЕТСЯ и деструктор вызывается только
ОДИН РАЗ!
Фокус в том, Стандарт позволяет компилятору проводить оптимизацию многочисленных вызовов конструкторов. Видимо, наличие явно определенного конструктора копирования(как и виртуально деструктора) говорит компилятору о возможной оптимизации.