2009-06-11 3 views
7

Этот код не работает во время выполнения в конструкторе копирования.
Но компилятор (MSVS2008) не выдает никаких предупреждений.Является самоинициализацией 'A a = a;' позволил?

Не могли бы вы объяснить (желательно со ссылкой на стандарт), является ли этот код незаконным или что?

Я понимаю, что A a = a; никогда не должен быть написан на первом месте, , но я ищу теоретический фон.

class A 
{ 
public: 

    A() 
    :p(new int) 
    { 
    } 

    A(const A& rv) 
    { 
     p = new int(*rv.p); 
    } 

    ~A() 
    { 
     delete p; 
    } 


private: 

    int *p; 
}; 

int main() 
{ 
    A a = a; 
} 
+0

Как я понимаю из ответов, этот код является законным. Должен ли я добавить утверждение или если (& rv == this) p = NULL; здесь, чтобы быть в безопасности? Кто-нибудь справляется с этой проблемой в своем коде? – Yarik

ответ

8

Ваш код не вызывает стандартный конструктор, но конструктор копирования, поэтому вы получаете доступ к неинициализированному указателю.

+0

Любые цитаты из стандарта об этом являются ошибкой времени компиляции или нет? –

+4

Это действительно не может быть ошибкой времени компиляции. Вот два условия, вызывающие проблему: * Ваш конструктор копирования отменяет указатель на исходный объект. * Вы инициализируете объект самим. Эти две проблемы могут быть в совершенно разных единицах компиляции, и компилятору необходимо будет увидеть оба условия, чтобы помечать ошибку. Каждое условие не является проблемой само по себе, поэтому нет причин для компилятора жаловаться. – JohnMcG

+1

@JohnMcG: Я не согласен ... Объявление переменных и инициализация не могут быть в разных единицах компиляции. Это явно не имеет никакого смысла. К сожалению, комитет C++ никогда не думал об этом (или хотя это не важно .. что угодно). Вы можете утверждать, что компилятор является стандартным компрометирующим, и вы правы в этом sanse, но IMHO - это ошибка стандарта. –

1

A a = a; определенно не следует писать. Но можно было написать a = a. Ваш оператор назначения должен проверить на &rv == this и ничего не делать в случае самокопирования.

Ах, да, вы должны написать оператор присваивания для класса А.

+0

Извините, я исправил ответ. – Dima

+0

№. Оператор присваивания можно оптимизировать и вместо этого заменить конструкцией копирования. –

+0

Марк, и что, если у вас есть A a; a = a; ? Будет вызываться конструктор по умолчанию для A, а затем a :: operator =(). Я сомневаюсь, что он будет оптимизирован. – Dima

3

В соответствии со стандартом (12.6.1 [class.expl.init]) инициализация само является совершенно законным.

Таким образом, следующее правовое.

A a = a; 

Вам просто нужно написать свой конструктор копирования, чтобы справиться с ним правильно.

A(const A& rv) 
{ 
    if(&rv == this) { 
     p = new int(0); 
     return; 
    } 

    p = new int(*rv.p); 
} 

Редактировать: Обновлен код на основе комментариев.

+0

Ну, «правильно» - это зависит от ваших намерений. Таким образом, вы получите неинициализированный объект. – Suma

+2

Помните, что 'a' не инициализируется при передаче в качестве параметра его собственного конструктора копирования. Таким образом, «A :: p» не определен, и этот конструктор копирования не инициализирует его. –

+0

Чтобы добавить: ваша первая часть ответа о стандарте верна, однако вторая я считаю сомнительной. Хотя вы избежите аварии здесь, в результате не инициализации p, вы, скорее всего, получите крах позже. – Suma

4

Интересное чтение на самолечение назначение: http://www.gotw.ca/gotw/011.htm

В частности, обратите внимание, «Постскриптум # 1» в связи с этим вопросом, и некоторые из ответов.

+0

[О, мой ...] (http://ideone.com/uEECOs) –