tags:

views:

193

answers:

4

This code fails at runtime in the copy constructor. But the compiler (MSVS2008) issues no warnings.

Could you explain (preferably cite the standard) whether this code is illegal or what?

I understand that A a = a; should never be written at the first place, but I am looking for a theoretical background.

 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;
 }
+6  A: 

Your code is not calling the standard constructor but the copy constructor, so you are accessing an uninitialized pointer.

Artur Soler
Any citations from the standard about this being a compile-time error or not?
Mehrdad Afshari
It really can't be a compile-time error.Here are the two conditions triggering the problem:* Your copy constructor de-references a pointer from the source object.* You are initializing an object with itself.These two problems could be in completely different compilation units, and the compiler would need to see both conditions to flag an error. Each condition is not a problem in itself, so there's no reason for the compiler to complain.
JohnMcG
+1  A: 

A a = a; definitely should not be written. But a = a could be written. Your assignment operator must check for &rv == this and do nothing in case of a self-copy.

Oh, yes, you do need to write an assignment operator for class A.

Dima
Sorry, I've fixed the answer now.
Dima
No. The assignment operator can be optimised away and replaced with copy construction instead.
Martin York
Mark, and what if you have A a; a = a; ? The the default constructor for A would be called, and then a::operator=(). I doubt it will be optimized away.
Dima
Martin's point is that checking for self-assignment in the assignment operator may be necessary to prevent something like this, but it is not sufficient.Because it is possible for assignment to be optimized into the copy constructor, the copy constructor also needs to be accounted for.
JohnMcG
+4  A: 

According to the standard (12.6.1 [class.expl.init] ) self initialization is perfectly legal.

Therefore the following is legal.

A a = a;

You just need to write your copy constructor to deal with it correctly.

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

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

Edit: Updated code based on comments.

Glen
Well, "correctly" - this depends on what your intentions are. This way you will end up with uninitialized object.
Suma
Remember that 'a' is not initialised when passed as a parameter to its own copy constructor. Thus 'A::p' is undefined and this copy constructor does not initialize it.
Martin York
To add: your first part of the reply about the standard is correct, however the 2nd I find dubious. While you will avoid the crash here, as a result of p not initialized you will most likely get crash later.
Suma
Updated the copy constructor code to initialise p to something useful
Glen
Checking for self-copy only makes sense in the operator=(), but not in the copy constructor. See Fred Larson's answer.
Dima
+2  A: 

Interesting reading on self-assignment: http://www.gotw.ca/gotw/011.htm

In particular, note "Postscript #1" in relation to this question and some of the answers given.

Fred Larson