C++/Classes & Members
Expert: Ralph McArdell - 8/29/2008
QuestionIs there a way to have one class include another class?
Say you have class_A, class_B and class_C, and you have data_class_A, data_class_B and data_class_C.
You want class_A to have data_class_B and data_class_C.
You want class_B to have data_class_A and data_class_C.
You want class_C to only have data_class_B.
I looked and friends and child classes but not sure that would achieve what I'm after. Can it be done?
AnswerAlmost certainly. It depends on what exactly you mean by the term "have one class include another" and "to have".
Assuming you mean can you have a class whose instances (i.e. objects) contain data parts that are instances (i.e. objects) of other classes then the answer is of course "yes":
class class_A
{
data_class_B bData_;
data_class_C cData_;
// ...
};
class class_B
{
data_class_A aData_;
data_class_C cData_;
// ...
};
class class_C
{
data_class_B bData_;
// ...
};
In these cases the containing classes (class_A, class_B, class_C) only have public access to members of the class type data members they contain (data_class_A, data_class_B and data_class_C). If you require more access first review your design to see why you would require such access as often such requirements are failures of the design, and if you still feel a valid case can be made make the containing class a friend of the contained class in question.
If the any of the classes class_A, class_B or class_C can be said to be variations that specialise the behaviour of any of the data classes (data_class_A, data_class_B and data_class_C) then they should be sub classes of that data class. For example if you can say that class_C is a data_class_B specialisation then publicly derive class_C from data_class_B:
class class_C : public data_class_B
{
// ...
};
In this case class_C will get public and protected access to its data_class_B part.
Note however that in general classes have to be designed to be base classes that are meant to be derived from so if the data classes are not designed to be so then they should almost certainly not be used as base classes.
Depending on what you are doing you may prefer to hold references to data class members as pointers to those class objects. This might be the case if any of the data class object members of class_A, class_B or class_C objects are associated with are only known at the time of object creation, or maybe can be added to changed during the lifetime of the object, for example:
class class_C
{
data_class_B * bData_;
// ...
public:
class_C( data_class_B & the_data )
: bData_(&the_data)
{
}
UpdateData( data_class_B & the_data )
{
bData_ = &the_data;
}
// ...
};
In such cases it important to know who owns the associated object.
[note: ownership implies responsibility for deleting an object]
In the above case although I have stored the reference to the data_class_B by pointer I pass the reference by C++ reference to the constructor of class_C. This would indicate that the object referenced may not have been created on the heap using new and therefore requires no explicit deletion and so objects of class_C do not own their referenced data_class_B objects.
If we were expecting a dynamically created object reference then I would have specified the reference be passed to the class_C constructor by pointer as this is what we get from new. Whether passing such an object reference (pointer) also transfers ownership would need to be clear and well documented,
The other point to be aware of is whether class_C objects share the referenced data_class_B object. If they take ownership of the referenced data_class_B object then presumably the data_class_B referenced should only be destroyed (deleted) after or during the destruction of the last class_C object referring to the given data_class_B object. In such cases reference counted smart pointers are useful such as the TR1 addition to the standard library std::tr1::shared_ptr (see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1745.pdf for more on the TR1 update to the C++ standard library).
[note: not many C++ implementations currently include the TR1 additions so we can use the Boost (
http://www.boost.org/) smart pointers instead (
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/smart_ptr.htm), and their tr1 support (
http://www.boost.org/doc/libs/1_36_0/doc/html/boost_tr1.html)].
So in summary: yes you can include objects of one class in the data portion of another class's instances (and even as 'class' data if they are specified as static), and you can, if appropriate, derive one class from one or more others. Exactly which solution is best for a specific situation depends on the requirements and design criteria in force at the time.
For more on C++ and inheritance and object oriented design I suggest you look at Scott Meyers "Effective C++", items 35 - 44 (in the second edition) for starters - in fact any serious C++ developer should read this book from cover to cover and keep it close for reference as it has many good guidelines.
Finally please note that the code shown here are only fragments used to demonstrate specific points made in the answer text. They are not intended to be complete and may contain errors and typos - if so then I apologise.
Hope this helps. If not or you require further clarification then please ask another (possibly follow up) question.