假设我们有两个类A
和B
。当使用合成来模拟“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;
};
忽略设计中的任何缺陷(这只是一个人为举例说明我的问题),我不得不繁琐地复制ContactInfo
中Person
的确切函数签名。在一个更复杂的例子中,可能有许多这样的函数,以及许多组合类的层,导致代码重复,包括所有常见的维护问题和容易出错等等。
尽管如此,建议的做法是根据第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
)的答案将是理想的。< / p>
编辑:为了澄清,为了回应一些答案,我知道私有继承作为建模组合的替代“is-implemented-in-terms-of “。我的问题与此无关,它具体涉及维基百科关于特征的陈述的含义。我不是要求组成的替代品。我已经加粗了我的问题,以便更清楚地知道这就是我所要求的。
答案 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;
}
这是:一个编译时模板化的类,您可以在其中实现(和/或专门化)您的委托方法,以便在没有(无论出于何种原因)重复继承的情况下转发您需要的任何内容。