Composition syntax
Actually, you’ve been using
composition all along to create classes. You’ve just been composing
classes primarily with built-in types (and sometimes strings). It turns
out to be almost as easy to use composition with user-defined
types.
Consider a class that is valuable for
some reason:
//: C14:Useful.h
// A class to reuse
#ifndef USEFUL_H
#define USEFUL_H
class X {
int i;
public:
X() { i = 0; }
void set(int ii) { i = ii; }
int read() const { return i; }
int permute() { return i = i * 47; }
};
#endif // USEFUL_H ///:~
The data members are private in
this class, so it’s completely safe to embed an object of type X as
a public object in a new class, which makes the interface
straightforward:
//: C14:Composition.cpp
// Reuse code with composition
#include "Useful.h"
class Y {
int i;
public:
X x; // Embedded object
Y() { i = 0; }
void f(int ii) { i = ii; }
int g() const { return i; }
};
int main() {
Y y;
y.f(47);
y.x.set(37); // Access the embedded object
} ///:~
Accessing the member functions of the
embedded object (referred to as a
subobject) simply requires another member
selection.
It’s more common to make the
embedded objects private, so they become part of the underlying
implementation (which means you can change the implementation if you want). The
public interface functions for your new class then involve the use of the
embedded object, but they don’t necessarily mimic the object’s
interface:
//: C14:Composition2.cpp
// Private embedded objects
#include "Useful.h"
class Y {
int i;
X x; // Embedded object
public:
Y() { i = 0; }
void f(int ii) { i = ii; x.set(ii); }
int g() const { return i * x.read(); }
void permute() { x.permute(); }
};
int main() {
Y y;
y.f(47);
y.permute();
} ///:~
Here, the permute( ) function
is carried through to the new class interface, but the other member functions of
X are used within the members of
Y.