2.14.  Subobjects

[ fromfile: subobject.xml id: subobject ]

An object can contain another object, in which case the contained object is considered to be a subobject. In Example 2.22, each Square has two Point subobjects.

Example 2.22. src/subobject/subobject.h

[ . . . . ]
class Point {
 public:
    Point(int xx, int yy) : m_x(xx), m_y(yy){}
    ~Point() {
       cout << "point destroyed: ("
            << m_x << "," << m_y << ")" << endl;
    }
 private:
    int m_x, m_y;
};

class Square {
 public:
    Square(int ulx, int uly, int lrx, int lry)
    : m_UpperLeft(ulx, uly), m_LowerRight (lrx, lry)  1
    {}

    Square(const Point& ul, const Point& lr) :
    m_UpperLeft(ul), m_LowerRight(lr) {}              2
 private:
    Point m_UpperLeft, m_LowerRight;                  3
};

[ . . . . ]

1

Member initialization is required here because there is no default ctor.

2

Initialize members using the implicitly generated Point copy ctor.

3

Embedded subobjects.

<include src="src/subobject/subobject.h" href="src/subobject/subobject.h" id="squarepoint" mode="cpp"/>


Whenever an instance of Square is created, each of its subobjects is created with it so that all three objects occupy a contiguous chunk of memory. When a Square instance is destroyed, all its subobjects are also destroyed.

The Square is composed of two Point objects. Because Point has no default constructor[33] you must properly initialize each Point subobject in the member initialization list of Square. [34]

Example 2.23 is the client code which creates instances of the classes discussed.

Example 2.23. src/subobject/subobject.cpp

#include "subobject.h"

int main() {
    Square sq1(3,4,5,6);
    Point p1(2,3), p2(8, 9);
    Square sq2(p1, p2);
}

<include src="src/subobject/subobject.cpp" href="src/subobject/subobject.cpp" id="subobjectcpp" mode="cpp"/>


Even though no destructor was defined for Square, each of its Point subobjects were properly destroyed whenever the containing object was. As you observed in Section 2.5.1, this is an example of composition, which is discussed in more detail in Section 6.9.

point destroyed: (8,9)
point destroyed: (2,3)
point destroyed: (8,9)
point destroyed: (2,3)
point destroyed: (5,6)
point destroyed: (3,4)


[33] Why not?

[34] Why can't you simply initialize m_x and m_y here?