Состав: использование черт, чтобы избежать пересылки функций?

Допустим, у нас есть два класса, A а также B, При использовании композиции для моделирования «имеет» или же «Является реализованными в-плана, из» отношения (например, B имеет A), один из недостатков против наследования заключается в том, что B не содержит публичную функциональность A что для этого требуется. Для того, чтобы получить доступ к AПубличные функции, необходимо обеспечить функции пересылки (в отличие от наследования, где B унаследует все Aпубличные функции).

Чтобы дать более конкретный пример, скажем, у нас есть Person который имеет ContactInfo:

using namespace std;

class ContactInfo
{
public:
ContactInfo();
void updateAddress(string address);
void updatePhone(string phone);
void updateEmail(string email);
private:
string address;
string phone;
string email;
};

class Person
{
public:
Person();
// Forwarding functions:
void updateAddress(string address){contactInfo.updateAddress(address)};
void updatePhone(string phone){contactInfo.updatePhone(phone)};
void updateEmail(string email){contactInfo.updateEmail(email)};
private:
ContactInfo contactInfo;
};

Игнорируя любые недостатки в дизайне (это просто надуманный пример, чтобы продемонстрировать мой вопрос ниже), мне пришлось утомительно копировать точные сигнатуры функций из ContactInfo в Person, В более сложном примере может быть много таких функций и многоуровневых составных классов, что приводит к значительному дублированию кода со всеми обычными проблемами обслуживания и подверженности ошибкам и т. Д.

Тем не менее, это рекомендуемая практика для моделирования «имеет» или же «Является реализованными в-плана, из» согласно источникам, таким как Пункт 38 Эффективного C ++ Мейерса, и Пункт 24 Исключительного C ++ Саттера (ссылка на сайт).

Исследуя это, я наткнулся на эта статья в Википедии который обсуждает ту же тему. В нижней части статьи, это предполагает следующее:

Одним из недостатков использования композиции вместо наследования является то, что все
методы, предоставляемые составными классами, должны быть
реализовано в производном классе, даже если они только переадресация
методы. […] Этого недостатка можно избежать, используя черты.

Я довольно плохо знаком с понятием черт и, учитывая все прочитанное, мне трудно соотноситься с приведенным выше утверждением. Поэтому мой вопрос: Как можно использовать черты, чтобы избежать перенаправления функций с композицией? Ответ основан на моем примере (Person а также ContactInfo) было бы идеально.

РЕДАКТИРОВАТЬ: Просто чтобы прояснить, в ответ на некоторые ответы я знаю о частном наследовании как об альтернативе композиции для моделирования «Является реализованными в-плана, из». Мой вопрос не об этом, а конкретно о значении заявления Википедии, касающегося черт характера. Я не прошу альтернативы композиции. Я выделил свой вопрос, чтобы прояснить, что это то, что я спрашиваю.

8

Решение

Прежде всего, я должен отметить, что признаки — это разные вещи в C ++ / STL и таких языках, как PHP, Lasso и т. Д. Похоже, что статья из Википедии ссылается на PHP-подобные признаки, потому что признаки C ++ / STL не предназначены для полиморфного повторного использования (мы говоря о повторном использовании кода с полиморфным поведением, верно?). Они предназначены для упрощения объявления шаблонных классов.

Черты используются в некоторых языках, которые не поддерживают множественное наследование (PHP, Lasso и т. Д.). Черты позволяют «эмулировать» множественное наследование (но множественное наследование и черты не совсем совпадают).

В отличие от C ++ не поддерживает PHP-черты, но поддерживает множественное наследование. Так что, если говорить о C ++, то решение, похожее на черту, будет примерно таким:

class VisibleTrait
{
public:
virtual void draw();
};

class SolidTrait
{
public:
virtual void collide(Object objects[]);
};

class MovableTrait
{
public:
virtual void update();
};// A player is visible, movable, and solid
class Player : public VisibleTrait, MovableTrait, SolidTrait
{
};

// Smoke is visible and movable but not solid
class Smoke : public VisibleTrait, MovableTrait
{
};

// A hause is visible and solid but not movable
class House : public VisibleTrait, SolidTrait
{
};

Итак, чтобы ответить на ваш вопрос

Как можно использовать черты, чтобы избежать пересылки функций с
состав?

1) Черты не избегают пересылки функций с композицией, потому что черты работают независимо от композиции. (Статья из Википедии немного вводит в заблуждение относительно отношений между чертами и составом)
2) PHP / Lasso-подобные черты могут быть частично эмулированы в C ++ с множественным наследованием.

1

Другие решения

Может быть, вы можете попробовать частное наследование:

class Person : private ContactInfo
{
public:
Person() { }
using ContactInfo::updateAddress;
using ContactInfo::updatePhone;
using ContactInfo::updateEmail;
};

int main()
{
Person person;
person.updateAddress("hi");
return 0;
}

Хотя вы можете не упускать из виду предостережения, перечисленные в этом Часто задаваемые вопросы:

Есть также несколько различий:

  • Вариант простой композиции необходим, если вы хотите содержать несколько двигателей на автомобиль
  • Вариант частного наследования может привести к ненужному множественному наследованию
  • Вариант частного наследования позволяет членам Car преобразовать автомобиль * в двигатель *
  • Вариант частного наследования позволяет получить доступ к защищенным членам базового класса.
  • Вариант частного наследования позволяет Car переопределять виртуальные функции Engine
  • Вариант приватного наследования упрощает (20 символов по сравнению с 28 символами) метод Car ()
    это просто вызывает метод start () Engine

В остальном пример представленной композиции кажется идентичным вашему. Нажатие на ссылку черты в статье в Википедии не предоставило никаких статей C ++, и ссылка в ссылках, кажется, о type traits, Я не мог найти как type traits имеет какое-либо отношение к вашему сценарию.

2

В статье рассказывается о наследовании с Интерфейс,

так на самом деле это говорит о том, что объект должен уважать некоторые подписи.

черты типа можно использовать для проверки правильности подписи и отправки в соответствующую функцию

Например, некоторые алгоритмы STL ждут тип Итератор,
но эти итераторы не наследуют от class Iterator но должен предоставить какой-то контракт (operator ++(), operator !=(rhs), operator*()).

с примером статьи:

  • Класс Player — который может двигаться
  • Класс здания — который не может двигаться

И код:

#if 1
// simple type traits which tells if class has method update_position
template <typename T> struct can_move;

// Hardcode the values
template <> struct can_move<Player> { static const bool value = true; };
template <> struct can_move<Building> { static const bool value = false; };

#else
// or even better, but need a has_update_position. (see how to check if member exist in a class)
template <typename T> struct can_move{ static const bool value = has_update_position<T>::value };
#endif

template <typename T, bool> struct position_updater;

// specialization for object which can move
template <typename T> struct position_updater<T, true>
{
static void update(T& object) { object.update_position(); }
};

// specialization for object which can NOT move
template <typename T> struct position_updater<T, false>
{
static void update(T& object) { /* Do nothing, it can not move */ }
};template <typename T>
void update_position(T& object)
{
// statically dispatch to the correct method
// No need of interface or inheritance
position_updater<T, can_move<T>::value>::update(object);
}
1

AFAIK класс черт что-то вроде следующего:

#include <iostream>
using namespace std;

class ContactInfo
{
public:
void updateAddress() { cout << "update address"; };
void updatePhone() {};
void updateEmail() {};
};

template<class T> class TraitClass
{
public:

private:
T obj;
};

template<> class TraitClass<ContactInfo>
{
public:
void updateAddress() {obj.updateAddress();};
void updatePhone() {obj.updatePhone();};
void updateEmail() {obj.updateEmail();};
private:
ContactInfo obj;
};

class Person
{
public:
void updateAddress() {obj.updateAddress();};
void updatePhone() {obj.updatePhone();};
void updateEmail() {obj.updateEmail();};
private:
TraitClass<ContactInfo> obj;
};

int main() {

Person myPerson;

myPerson.updateAddress();

return 0;
}

То есть: шаблонный класс во время компиляции, где вы можете реализовать (и / или специализировать его) ваши методы делегата, чтобы пересылать все, что вам нужно, без (по какой бы то ни было причине) повторения наследования.

http://en.wikipedia.org/wiki/Trait_(computer_programming)

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector