Бьерн Страуструп - Язык программирования С++. Главы 11-13
Страница 33. Отношения принадлежности



12.2.4 Отношения принадлежности

Если используется отношение принадлежности, то существует два основных
способа представления объекта класса X:
[1] Описать член типа X.
[2] Описать член типа 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) { }
          // ...
       };

 
« Предыдущая статья   Следующая статья »