【发布时间】:2014-01-22 02:01:16
【问题描述】:
假设我们有两个类,A 和 B。当使用组合来建模 "has-a" 或 "is-implemented-in-terms-of" 关系时(例如 B has-a A),与继承相比的缺点之一是 B 不包含它需要的 A 的公共功能。为了访问As 的公共函数,必须提供转发函数(与继承相反,B 将继承As 的所有公共函数)。
举一个更具体的例子,假设我们有一个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;
};
忽略设计中的任何缺陷(这只是一个人为的例子来证明我的问题),我不得不在Person 中繁琐地复制来自ContactInfo 的确切函数签名。在一个更复杂的例子中,可能会有许多这样的函数,以及许多层的组合类,这会导致大量代码重复,以及所有常见的维护问题和容易出错等。
尽管如此,这是根据条款 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,建议如下:
使用组合代替继承的一个缺点是所有 由组合类提供的方法必须是 在派生类中实现,即使它们只是转发 方法。 [...] 这个缺点可以通过使用特征来避免。
我对特质的概念还很陌生,而且根据我所阅读的所有内容,我发现很难与上述陈述相关联。因此,我的问题是:如何使用特征来避免通过组合转发函数?基于我的示例(Person 和 ContactInfo)的答案将是理想的。
编辑:为了澄清,为了回应一些答案,我知道私有继承是建模的替代组合"is-implemented-in-terms-of "。我的问题不是关于那个,而是关于维基百科关于特征的声明的含义。我不是在要求作曲的替代品。我加粗了我的问题,以便更清楚地表明这就是我要问的。
【问题讨论】:
-
我认为维基百科在这方面误导了你。 Traits 是泛型编程的工具,比如说你想包含一些模板类型参数类型的
contactInfo。 -
“有”关系是“有”关系。没有“人为的例子”,因为在任何情况下,都可以解决决定某事是某事还是有某事的问题。如果您无法解决此问题,那么您在其他地方犯了错误。一个棘手的例子是这样的:汽车是交通工具,飞机是交通工具,那么火车也是交通工具?答案是不。火车是一组车辆(前置发动机、货车,也可以移动,也可以选择后置车辆)。你总是必须做出明确的区分,否则你会遇到麻烦。
-
我觉得这篇维基百科文章不好:-/
-
@lukasz1985 谢谢,但我的问题不是关于如何区分“has a”和“is a”,而是关于特征。
-
我认为您不应该使用特质来更轻松地完成工作。您可能弄乱了组合,因为在您的示例中,您应该能够通过使用包含类(如 Person->getContactInfo() )来获取底层对象。好处是不要先使用特征和多重继承。如果这样下去,唯一剩下的就是在继承和组合(IS A 或 HAS A)之间做出决定。您可以在这里查看我的答案:stackoverflow.com/questions/49002/…
标签: c++ oop aggregation composition traits