Problem
Based on the One Responsibility Principle, we usually want the data as a separate component from the implementation class. This implys an “is implemented in terms of” relationship.
Implement the “is implemented in terms of” relationship
-
Through Composition (most preferred way)
// In Data.cpp class Data{ public: explicit Data(): width(0), height(0){} public: int width; int height; // these two should be define since we want to control // the copy process by ourselves // copy constructor Data(const Data& data); // = operator Data& operator=(const Data& data); void loadData(...){...} }; // In DataConsumer.cpp #include "Data.cpp" class DataConsumer{ explicit DataConsumer(): data(Data()){} ... Data data; // composition happened here ... void printDataWidth(){cout << data.width << endl;} };
Note: Load data can be non-member function or in the implementation class as well. It is not necessary to be handled by data class.
-
Private Inheritence
// In Data.cpp class Data{ public: explicit Data(): width(10), height(0){} public: int width; int height; // these two should be define since we want to control // the copy process by ourselves // copy constructor Data(const Data& data); // = operator Data& operator=(const Data& data); void loadData(...){...} }; // In DataConsumer.cpp #include "Data.cpp" class DataConsumer : private Data{ explicit DataConsumer(): Data(){} ... void printDataWidth(){cout << data.width << endl;} };
Private inheritance doesn’t mean is-a. In contrast to public inheritance, compilers will generally not convert a derived class object into a base class object if the inheritance relationship between the classes is private. Private class is used to make an “is implemented in terms of” relationship.
When is it need?
When you’re dealing with two classes not related by is-a where one either needs access to the protected members of another or needs to redefine one or more of its virtual functions.
-
Through nested class composition
// In Data.cpp class Data{ public: explicit Data(): width(0), height(0){} public: int width; int height; // these two should be define since we want to control // the copy process by ourselves // copy constructor Data(const Data& data); // = operator Data& operator=(const Data& data); virtual void loadData(...){...} }; // In DataConsumer.cpp #include "ParentData.cpp" #include <iostream> using namespace std; class DataConsumer{ public: explicit DataConsumer(): data(Data()){} void printDataWidth(){cout << data.width << endl;} void printDatauMin(){cout << data.uMin << endl;} private: // declare a nested data class that // publicly inheriented from parentData class Data : public ParentData{ public: explicit Data(): ParentData(), uMin(11), uMax(20){} public: int uMin; int uMax; // these two should be define since we want to control // the copy process by ourselves // copy constructor Data(const Data& data); // = operator Data& operator=(const Data& data); public: virtual void loadData(...){...} }; Data data; };
Two reason to use this method over the private inheritance:
- Design
DataConsumer
to allow for derived classes, but prevent derived classes from redefiningloadData
. - Minimize
DataConsumer
’s compilation dependencies.
IfDataConsumer
inherits fromParentData
,ParentData
’s definition must be available whenDataConsumer
is compiled, so the file definingDataConsumer
probably has to #includeParentData.h
.
On the other hand, ifData
is moved out ofDataConsumer
andDataConsumer
contains only a pointer to aData
,DataConsumer
can get by with a simple declaration for theData
class; it need not #include anything to do withParentData
- Design