组成:使用特征来避免转发功能?

时间:2014-01-04 10:47:10

标签: c++ oop aggregation composition traits

假设我们有两个类AB。当使用合成来模拟“has-a”“is-implemented-in-terms-of”关系时(例如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;
};

忽略设计中的任何缺陷(这只是一个人为举例说明我的问题),我不得不繁琐地复制ContactInfoPerson的确切函数签名。在一个更复杂的例子中,可能有许多这样的函数,以及许多组合类的层,导致代码重复,包括所有常见的维护问题和容易出错等等。

尽管如此,建议的做法是根据第38项等来源建模“has-a”“is-implemented-in-terms-of” Meyers的Effective C ++,以及Sutter的Exceptional C ++(link)的第24项。

在研究这个问题时,我遇到了this Wikipedia article,它讨论了相同的主题。 At the bottom of the article,它建议如下:

  

使用组合代替继承的一个缺点是所有   组合类提供的方法必须是   在派生类中实现,即使它们只是转发   方法。 [...]使用特征可以避免这个缺点。

我对于特征的概念和我读过的所有内容都相当新,我发现很难与上述陈述联系起来。因此,我的问题是:如何使用特征来避免使用合成转发函数?基于我的示例(PersonContactInfo)的答案将是理想的。< / p>

编辑:为了澄清,为了回应一些答案,我知道私有继承作为建模组合的替代“is-implemented-in-terms-of “。我的问题与此无关,它具体涉及维基百科关于特征的陈述的含义。我不是要求组成的替代品。我已经加粗了我的问题,以便更清楚地知道这就是我所要求的。

4 个答案:

答案 0 :(得分:2)

也许你可以尝试私有继承:

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

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

虽然您可能需要注意此FAQ中列出的警告:

  

还有一些区别:

     
      
  • 如果您想要包含多个每辆车的引擎
  • ,则需要简单组合变体   
  • 私有继承变体可以引入不必要的多重继承
  •   
  • 私有继承变体允许Car的成员将Car *转换为Engine *
  •   
  • 私有继承变体允许访问基类的受保护成员
  •   
  • 私有继承变体允许Car覆盖Engine的虚函数
  •   
  • 私有继承变体使得它更简单(20个字符与28个字符相比),以赋予Car一个start()方法   只需调用Engine的start()方法
  •   

否则提供的构图示例与您的相同。点击维基百科文章中的特征链接没有提供任何C ++文章,参考文献中的链接似乎是type traits。我找不到type traits与你的情景有什么关系。

答案 1 :(得分:1)

文章讨论了继承与接口

所以实际上它告诉对象必须尊重某些签名。

类型特征可用于检查签名是否正确并分派到适当的函数

例如,某些STL算法等待 Iterator 类型, 但是这些迭代器不会从class Iterator继承但必须提供一些合同(operator ++()operator !=(rhs)operator*())。

文章示例:

  • 类播放器 - 可以移动
  • 类构建 - 无法移动

代码:

#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);
}

答案 2 :(得分:1)

首先,我要提一下,C ++ / STL和PHP,Lasso等语言中的特征是不同的东西。看起来维基百科上的文章指的是类似PHP的特征,因为C ++ / STL特征不是为多态重用而设计的(我们正在谈论具有多态行为的代码重用,对吧?)。它们旨在简化模板类的声明。

Traits用于某些不支持多重继承的语言(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)特征不会避免具有合成的转发功能,因为特征独立于合成而工作。 (来自Wikipadia的文章对于特征和构图之间的关系有点误导) 2)PHP / Lasso类特征可以在C ++中部分模拟,具有多重继承。

答案 3 :(得分:0)

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)