5

On Saturday I have an exam and I'm going through the past papers and I've encountered question according to C++ code:

class Car {
     char *LicencePlate;
     int age;
public:
     Car(const Car &); //this declaration
     ~Car();
}

What is the purpose of the declaration on line 5? In general what functionality should the implementation of a declaration of this nature provide? Write the code required for the implementation of the declaration on line 5 as it would appear in the Car.cpp file given the information provided.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880

5 Answers5

9

Let's go through this.

As you can tell from the name, that's a constructor. Because it takes a parameter a reference to an object of the same type, it's a copy constructor (C++ nomenclature).

As you know (or not), if you don't have a copy constructor, the compiler will generate one for you. The compiler generated copy constructor does a shallow copy.

Why you want to implement your own:

class Car {
     char *LicencePlate;
public:
     Car(char* plate, int size)
     {
        LicencePlate = new char[size];
        strcpy(LicencePlate, plate);
     }
     ~Car()
     {
        delete[] LicencePlate;
     }
};

I've modified your class a bit to better explain. Your class manages memory now. It allocates memory for LicencePlate. This is the scenario where you don't have a copy constructor. Say you do:

Car a("abc",3);

The compiler generated copy constructor is called with:

Car b(a);

But remember, this only does a shallow copy. So, actually, a.LicencePlate == b.LicencePlate. Can you see anything wrong with that?

When a goes out of scope, the destructor is called, and a.LicencePlate is deleted. But then you run into undefined behavior when b goes out of scope, because b's destructor will try to delete the same memory (remember, the two pointer point to the same memory because a shallow copy was created).

To avoid this, you define your own copy constructor:

class Car {
     char *LicencePlate;
     int sz;
public:
     Car(char* plate, int size)
     {
        LicencePlate = new char[size+1]();
        strcpy(LicencePlate, plate);
        sz = size;
     }
     Car(const Car& other)
     {
        LicencePlate = new char[other.sz+1]();
        sz = other.sz;
        strcpy(LicencePlate, other.LicencePlate);
     }
     ~Car()
     {
        delete[] LicencePlate;
     }
};

The rule of three means you should implement an assignment operator (you already have a copy constructor and a destructor). The motivation behind this is the same, only the problem replicates when you assign instead of initialize:

Car a("abc",3);
Car b;
b = a; //assignment - operator= is called

Now we're safe. b, when copied, will allocate new memory to hold the licence plate, so the double-delete can't occur.

I changed the code to demonstrate the point but you'll still have to put logic in there yourself.

Community
  • 1
  • 1
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 3
    Ah, the rule of three, usually stated as "use `std::string LicencePlate;` instead, dummy!". – Steve Jessop May 23 '12 at 21:38
  • +1, but where does `size` come from? It's not in the question, so I think we must be dealing in nul-terminated strings here. – Steve Jessop May 23 '12 at 21:43
  • @SteveJessop ah sorry forgot about that. I prefer this to `strlen`, but regardless, not an important part of the answer. – Luchian Grigore May 23 '12 at 21:44
  • To be honest I can't blame you for adding a data member. Since the class in the question lacks a usable constructor that actually establishes any class invariants, we could just as well say that the default copy constructor and destructor are fine, and it's the client's responsibility to manage the memory pointed to by `LicencePlate`. But I don't think I'd get the mark for that in the exam. – Steve Jessop May 23 '12 at 21:46
8

It is a copy constructor, it's purpose is to make an exact copy of the object which was given as a parameter.

I will leave it to you to decide how best to do that.

Ben
  • 34,935
  • 6
  • 74
  • 113
  • 1
    Ah, depends on what you mean by **exact copy**. I'd say the compiler-generated copy constructor makes an exact copy - i.e. copies the pointer, not what it points to. But that's the source of the problem. To recap - the purpose of a copy constructor is **not** to make an exact copy, but a deep copy. – Luchian Grigore May 23 '12 at 21:51
2

That's a copy constructor declaration. It takes a reference to a constant Car, meaning you can read the passed in value, but can not (without dodgy casts) write it. This is just the canonical way of creating a new object by copying the original. You will probably want to do a strdup as part of the implementation.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
0

The declaration is referred to as copy constructor. Typically a copy constructor is used to construct a new instance of a given instance to a class.

In the signature:

Car(const Car &)

"const" implies that the instance passed cannot be modified in the method. "&" implies that an instance is passed into the method by reference. Passing in an instance of a class without an "&" (pass by value) would create a new instance of the class using the copy constructor. Not only will passing by value is self defeating in the case of declaration of copy constructor, but it would cause the copy constructor to be invoked recursively until stack overflow is encountered.

The code that goes into the cpp file should make an acceptable copy of an instance passed in. Acceptable in the way that makes sense based on the user scenario.

Vishnu Pedireddi
  • 2,142
  • 4
  • 25
  • 34
  • 1
    "Passing by value would be self defeating in the case of declaration of the copy constructor" - it would actually lead to an infinite loop and probably a stack overflow exception. Also, the purpose is not to make an exact copy. See my comment to Ben's answer. – Luchian Grigore May 23 '12 at 21:55
-3

I'm assuming you mean Line 5, then

Car(const Car &);

is a constructor whose only argument is a reference to another Car object. It's marked as const to show that the constructor shouldn't modify the given car. Since it's a reference, you'd need to check to make sure you don't somehow have a reference to the same object that's being constructed:

if (this==car){
  return;
}

Or something like that.

whiterook6
  • 3,270
  • 3
  • 34
  • 77