Отношения принадлежности
Если используется отношение принадлежности, то существует два основных способа представления объекта класса X:
- Описать член типа X.
- Описать член типа X* или X&.
Если значение указателя не будет меняться и вопросы эффективности не волнуют, эти способы эквивалентны:
class X { //... public: X(int); //... };
class C { X a; X* p; public: C(int i, int j) : a(i), p(new X(j)) { } ~C() { delete p; } };
В таких ситуациях предпочтительнее непосредственное членство объекта, как X::a в примере выше, потому что оно дает экономию времени, памяти и количества вводимых символов. Обратитесь также к §12.4 и §13.9.
Способ, использующий указатель, следует применять в тех случаях, когда приходится перестраивать указатель на "объект-элемент" в течении жизни "объекта-владельца". Например:
class C2 { X* p; public: C(int i) : p(new X(i)) { } ~C() { delete p; }
X* change(X* q) { X* t = p; p = q; return t; } };
Член типа указатель может также использоваться, чтобы дать возможность передавать "объект-элемент" в качестве параметра:
class C3 { X* p; public: C(X* q) : p(q) { } // ... }
Разрешая объектам содержать указатели на другие объекты, мы создаем то, что обычно называется "иерархия объектов". Это альтернативный и вспомогательный способ структурирования по отношению к иерархии классов. Как было показано на примере аварийного движущегося средства в §12.2.2, часто это довольно тонкий вопрос проектирования: представлять ли свойство класса как еще один базовый класс или как член класса. Потребность в переопределении следует считать указанием, что первый вариант лучше. Но если надо иметь возможность представлять некоторое свойство с помощью различных типов, то лучше остановиться на втором варианте. Например:
class XX : public X { /*...*/ };
class XXX : public X { /*...*/ };
void f() { C3* p1 = new C3(new X); // C3 "содержит" X C3* p2 = new C3(new XX); // C3 "содержит" XX C3* p3 = new C3(new XXX); // C3 "содержит" XXX //... }
Приведенные определения нельзя смоделировать ни с помощью производного класса C3 от X, ни с помощью C3, имеющего член типа X, поскольку необходимо указывать точный тип члена. Это важно для классов с виртуальными функциями, таких, например,как класс Shape (§1.1.2.5), и для класса абстрактного множества (§13.3).
Заметим, что ссылки можно применять для упрощения классов, использующих члены-указатели, если в течение жизни объекта-владельца ссылка настроена только на один объект, например:
class C4 { X& r; public: C(X& q) : r(q) { } // ... };