Copy constructors in Inheritance

From my book:
"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Box.h in Ex9_05
#pragma once
#include <iostream>
using std::cout;
using std::endl;

class CBox                   // Base class definition
{
  public:
    // Base class constructor
    explicit CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0) : m_Length(lv), m_Width(wv), m_Height(hv)
    {  std::cout << std::endl << "CBox constructor called";  }

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

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

  protected:
    double m_Length;
    double m_Width;
    double m_Height;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// CandyBox.h in Ex9_05
#pragma once
#include "Box.h"
#include <cstring>                               // For strlen() and strcpy()
#include <iostream>

class CCandyBox: public CBox
{
  public:
    char* m_Contents;

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

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

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

    // Derived class copy constructor
    CCandyBox(const CCandyBox& initCB)
     // Uncomment the following line to call the base class copy constructor 
      //: CBox(initCB)
    {
      std::cout << std::endl << "CCandyBox copy constructor called";

      // Get new memory
      m_Contents = new char[ strlen(initCB.m_Contents) + 1 ];

      // Copy string
      strcpy_s(m_Contents, strlen(initCB.m_Contents) + 1, initCB.m_Contents);
    }
/*      Uncomment this to add the move constructor
    // Move constructor
    CCandyBox(CCandyBox&& initCB): CBox(std::move(initCB))
    {
      std::cout << std::endl << "CCandyBox move constructor called";
      m_Contents = initCB.m_Contents;
      initCB.m_Contents = 0;
    }
*/
    // Destructor
    ~CCandyBox()    
    {
      std::cout << "CCandyBox destructor called" << std::endl;
      delete[] m_Contents;
    }
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Ex9_05.cpp
// Using a derived class copy constructor
#include <iostream>                    // For stream I/O
#include "CandyBox.h"                  // For CBox and CCandyBox
using std::cout;
using std::endl;

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;
}


This example produces the following output:

CBox constructor called
CCandyBox constructor2 called
CBox constructor called
CCandyBox copy constructor called
Volume of chocBox is 24
Volume of chocolateBox is 1
CCandyBox destructor called
CBox destructor called
CCandyBox destructor called
CBox destructor called


Although this looks okay at first sight, there is something wrong. The third line of output shows that the default constructor for the CBox part of the object chocolateBox is called, rather than the copy constructor. As a consequence, the object has the default dimensions rather than the dimensions of the initializing object, so the volume is incorrect. The reason for this is that when you write a constructor for an object of a derived class, you are responsible for ensuring that the members of the derived class object are properly initialized. This includes the inherited members."


Why must we call the base class constructor explicitly for it to work? Why does the default automatically call it then, why doesn't the copy constructor?

Also if I change the derived copy constructor to:

1
2
3
4
5
6
7
8
9
// Derived class copy constructor
CCandyBox(const CCandyBox& initCB): CBox(initCB)
{
std::cout << std::endl << "CCandyBox copy constructor called";
// Get new memory
m_Contents = new char[ strlen(initCB.m_Contents) + 1 ];
// Copy string
strcpy_s(m_Contents, strlen(initCB.m_Contents) + 1, initCB.m_Contents);
}


It will work right? Cause when I do "CBox(initCB)" it only sends the BASE part of the derived object to the base class copy constructor, is it copy or default?
Why must we call the base class constructor explicitly for it to work?

We don't always want the base class copy constructor to be called. If you are supplying the definition of a copy constructor, you're specifying the behavior of the constructor. So, specify the behavior.


Why does the default automatically call it then, why doesn't the copy constructor?

Is it really that surprising that the default constructor is invoked when you don't explicitly invoke a different one?
But then why does my book tell me do to that? It says make sure each constructor explicitly calls base constructor otherwise the argument the derived constructor wont be called.

The book is saying that if you need to use a base class constructor that isn't the default one, you must specify which constructor to use, with which arguments, in the initialization list.

If you don't specify it, it will use the default base class constructor, which doesn't take any arguments.
Last edited on
No MikeyBoy,

Its telling me that I need a base class constructor in the copy constructor that I call. Otherwise my copy constructor wont be able to be called. Try running the first code, the copy constructor isnt called.

But why?

Is it in default the compiler automatically calls both defaults?
Last edited on
Never mind I am a idiot, terribly sorry for this, I understand my mistake.
The base class constructor was being called, but the from the new derived class constructor, I was confused thinking the derived constructor initializes the heights/widths/heights so I thought it's default was called instead. Sorry for my fail
Topic archived. No new replies allowed.