Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Question about a derived class copy constructor!

Status
Not open for further replies.

pghsteelers

Technical User
Apr 21, 2006
121
US
I was hoping someone could explain the following issue regarding the need to call a copy constructor for the base part of the class in the initialization list for the copy constructor for the derived class (NOTE: complete code at bottom)- as pointed out as:

- A problem when initializing a new object by copying from an existing object
(i.e. classname newobject2(newobject1) )

- when writing a constructor for an object of a derived class, you are responsible for ensuring that the members of the derived class object are properly intialized - including inherited members.
(example:
//derived class copy constructor
CSample(const CSample& initCS): CBase(initCS)

My issue is that I don't quite understand "Why" if you don't do this, calling a copy constructor from the base in a derived class ends up just calling the constructor and not the copy constructor. See Code below for example:

#include <iostream> // For stream I/O
#include <cstring> // For strlen() and strcpy()
using namespace std;

// Listing 10_05-01
class CBox // Base class definition
{
public:
// Base class constructor
CBox(double lv = 1.0, double bv = 1.0, double hv = 1.0):
m_Length(lv), m_Breadth(bv), m_Height(hv)
{ cout << endl << "CBox constructor called"; }

// Copy constructor
CBox(const CBox& initB)
{
cout << endl << "CBox copy constructor called";
m_Length = initB.m_Length;
m_Breadth = initB.m_Breadth;
m_Height = initB.m_Height;
}

// CBox destructor - just to track calls
~CBox()
{ cout << "CBox destructor called" << endl; }

protected:
double m_Length;
double m_Breadth;
double m_Height;
};

// Listing 10_05-02
class CCandyBox: public CBox
{
public:
char* m_Contents;

// Derived class function to calculate volume
double Volume() const
{ return m_Length*m_Breadth*m_Height; }

// Constructor to set dimensions and contents
// with explicit call of CBox constructor
CCandyBox(double lv, double bv, double hv, char* str = "Candy")
:CBox(lv, bv, hv) // Constructor
{
cout << endl <<"CCandyBox constructor2 called";
m_Contents = new char[ strlen(str) + 1 ];
strcpy(m_Contents, str);
}

// Constructor to set contents
// calls default CBox constructor automatically
CCandyBox(char* str = "Candy") // Constructor
{
cout << endl << "CCandyBox constructor1 called";
m_Contents = new char[ strlen(str) + 1 ];
strcpy(m_Contents, str);
}

~CCandyBox() // Destructor
{
cout << "CCandyBox destructor called" << endl;
delete[] m_Contents;
}
};


int main()
{
CCandyBox chocBox(2.0, 3.0, 4.0, "Chockies"); // Declare and initialize
CCandyBox chocolateBox(chocBox); // Use copy constructor

cout << endl
<< "Volume of chocBox is " << chocBox.Volume()
<< endl
<< "Volume of chocolateBox is " << chocolateBox.Volume()
<< endl;

return 0;
}




The problem exist when the newobject2 gets the default constructor values rather than the values of the object it was trying to copy from. Stating that you have to
 
You haven't defined a copy constructor for CCandyBox, so the compiler generated a default copy constructor for you (which almost always does the wrong thing).

If you don't create one yourself and you try to use them, the compiler will create a default constructor, copy constructor and assignment operator.

If you want to make sure the compiler doesn't accidentally create one of those for you, you can define them as private. Ex.
Code:
class CSomething
{
...
private:
   // Defined private so they can't be used.
   CSomething();
   CSomething( const CSomething& );
   CSomething& operator( const CSomething& );
};
 
Thanks cpjust, however, I knew about a class not having a copy constructor defined so it will generate one for you that might not carry out the correct expectations.

However, what I was refering to is using a derived class, where it has inherited the members of a base class, one of those inherited members is a copy constructor as in my example above, but, when you call the copy constructor (i.e. CClassname Something2(something1) )

it doesn't call the copy constructor that you defined in the base class, it calls the constructor, the object that should have received the values set by the object it was being copied from, it actually receives the default values of the base class contructor. The answer being that you have to put in a copy constructor that calls the base copy constructor (i.e. CDerivedClass(const CDerived& initCB): CBaseClass(initCB).

I guess my confusion, is if the copy constructor doesn't have permissions issue, then its like the derived class is not receiving the copy constructor as a inherited member? Or can someone explain what is happening that you can rely on the copy constructor of the base class (takening into account it isn't a permissions issue?
 
I don't believe the compiler's default copy constructor is smart enough to call the base class copy constructor and therefore it assigns default values to the base class members. Although ArkM is the guy that has all the quotes from the C++ Standards document, so maybe he has a more solid answer?
 
Yes, the default copy constructor will call the base class copy constructor. Try this:
Code:
#include <iostream>

class Base
{
    int data;
public:
    Base() : data(0) { }
    Base(const Base& b) : data(b.data) { std::cout << "In Base Copy Constructor." << std::endl; }
};

class Derived : public Base
{
};

int main()
{
    Derived d1;
    Derived d2(d1);
}
It sounds like the original question is when you are writing your own copy constructor in the derived class, why doesn't the base class copy constructor get called automatically? The answer is that a copy constructor that you write is like any other constructor you write, you have to tell the compiler what to do. If you don't specify which base class constructor to call in the initialization list for any derived class constructor, the compiler will call the default constructor for the base class. There is no difference in this behavior between a regular constructor and a copy constructor in the derived class.
Code:
#include <iostream>

class Base
{
    int data;
public:
    Base() : data(0) { }
    Base(int i) : data(i) { }
    Base(const Base& b) : data(b.data) { std::cout << "In Base Copy Constructor." << std::endl; }
};

class Derived : public Base
{
public:
    Derived() : Base() { }
    Derived(const Derived& d) : Base(d) { }
    Derived(int i) : Base(i) { }
    Derived(double) { }
};

int main()
{
    Derived d1;
    Derived d2(d1);
    Derived d3(3);
    Derived d4(4.0);
}
Notice in this code how the first three constructors for Derived all call a Base class constructor explicitly. They can call any base class constructor they want (sort of). The fourth constructor for Derived doesn't call any, so the default constructor is called.

You don't inherit member functions, you inherit the ability to call them if you want. So if you are writing code and you want the base class copy constructor to be called, you have to call it.


cpjust said:
the compiler generated a default copy constructor for you (which almost always does the wrong thing)
While the compiler generated copy constructor does the wrong thing in that example, it almost always does the correct thing if you are using good C++ programming techniques. For example, you should use std::string instead of char* C style strings in CCandyBox. If that had been done, then the default copy constructor would have been fine.
 
For example, you should use std::string instead of char* C style strings in CCandyBox

in pghsteelers defence this is an example from Ivor Horton's Beggining Visual C++ 6.

A very good book for learning VC++

If somethings hard to do, its not worth doing - Homer Simpson
 
Yes, Its Horton's book. He is pretty good for explaining things, but sometimes (rarely) there is some assumptions made in a description of something.

 
Hey, I'm not trying to disparage anyone, just pointing out something I think is important. :)

That book is probably quite old, VC++ 6 came out 8 years ago and they're already on version 8.0. You are probably not learning good, modern C++ practices. That doesn't mean you can't be good at C++, but it does mean that you might have to unlearn some things in the future if you continue on as a C++ programmer.
 
Thanks uolj.

Unfortunately the only compiler available for me was VC++ 6. I would like to have 2005.

 
Which is probably why Java is such a attactive language for me right now =)
 
i wasnt having a dig [tongue], just pointing out that is was a learning excercise.

the reason for using the "char* C style strings" was to represent the potential problems of the default copy constructor when using derived classes.

If somethings hard to do, its not worth doing - Homer Simpson
 
That's surprising, especially since you can download the 2005 Express Editon for free. I think the book itself being old is a bigger issue, though. Most code in modern C++ beginner books (like Accelerated C++) will work ok even on VC++ 6.
 
ADoozer said:
the reason for using the "char* C style strings" was to represent the potential problems of the default copy constructor when using derived classes
Ahh, well that would be better (although I would be surprised if that book teaches std::string anyway). :)

Of course, as long as there is a copy constructor in the base class, it would be fine to use the default copy constructor in the derived class. As my very first example should show, the base class copy constructor will be called correctly.
 
Ah! Well I was turned onto this writer by finding his VC++ 2005 book and found it well laid out and nicely writen (for the little I skimmed over). So I found then that he wrote one for 6 and picked it up since that is what I use.

So you think that If I did pick up a book on VC 2005, that I should be able to conduct the coding out of the book?

In addition, I had heard the express edition, limits you, and I didn't know how much or where it would in comparison to doing a beginners C++ book.

Any thoughts? And thanks for all the advice!
 
There's a difference between learning C++ and learning C++ with Visual C++ specific stuff. I'm guessing the VC++ books teach VC++ specific stuff, which is more likely to not work across different versions. The regular C++ like what you have in your question here should work pretty well on either version.

I'm not familiar with Horton's books, so I don't know whether the Express Edition will work with them. I do know that standard C++ works fine in 2005 Express as long as you choose the right options to make sure you are getting regular C++ code and not managed C++ or any other .NET related stuff.
 
I have that book, and I love all the Wrox books. Most of the code in the book is for console apps anyways, so it should work fine. When you get into the Windows GUI stuff, you might need to convert what he's saying in the book to the way you use VC++ 2005.

uolj said:
Of course, as long as there is a copy constructor in the base class, it would be fine to use the default copy constructor in the derived class. As my very first example should show, the base class copy constructor will be called correctly.
But unfortunately in the CCandyBox code, the char* string is stored in the derived class, so the default copy constructor will do a shallow copy of just the pointer, not allocating a new string... As soon as the 2nd object goes out of scope -- BANG! The destructor tries to delete the pointer for the 2nd time, which is a very bad thing.
 
That is true. What that really demonstrates is the potential problem with using the default copy constructor in general and has nothing to do with base classes or derived classes. I guess my point was that in general, there should be no issue with using a default copy constructor in a derived class that is specific to it being a derived class, which is something the OP seems to be confused about.
 
Back to the original snippet: it calls CBox (base class) copy constructor! What's a problem?
Numbered listing (before inevitable crash;):
Code:
0: CBox constructor called
1: CCandyBox constructor2 called
2: CBox copy constructor called
3: Volume of chocBox is 24
4: Volume of chocolateBox is 24

Line 0: start to construct chocBox, CCandyBox passes dimentions to the base class constructor...
Line 1: construct derived object (oops: allocate memory and save the pointer - we need the special copy constructor and assignment operator to deal with this pointer - alas, no such members, and implicitly defined copy constructor will spread the pointer value to all objects)...
Line 2: OK, base class copy constructor is called via implicitly generated copy constructor of the CCandyBox...
Lines 3, 4: That's OK, we have two Chockies (and two deletable by destructor pointers to the one memory chunk)...

Yes, it's a very instructive work to quote:
The C++ Standard said:
The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The order of copying is the same as the order of initialization of bases and members in a user-defined constructor. Each subobject is copied in the manner appropriate to its type:
-- if the subobject is of class type, the copy constructor for the class is used;
-- if the subobject is an array, each element is copied, in the manner appropriate to the element type;
-- if the subobject is of scalar type, the built-in assignment operator is used.

Moral: if you have all members with proper copy constructors, don't worry about copy constructors in your class hierarchy. If you have a pointer type member, don't forget: it's scalar type object, so implicitly-defined member functions don't know about its target copy semantics.
 
Thank you all for the great input. I appeciate the help in understanding.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top