C++ 继承与聚合以及;has-a“;vs";is-a";。
在我的代码中,我发现使用类似mixin的继承来组合具有不同块的对象很有用。我有:C++ 继承与聚合以及;has-a“;vs";is-a";。,c++,design-patterns,inheritance,multiple-inheritance,C++,Design Patterns,Inheritance,Multiple Inheritance,在我的代码中,我发现使用类似mixin的继承来组合具有不同块的对象很有用。我有: class Name { public: typedef int32_t value_type; public: // ctors and dtors void set_value(value_type value) { value_ = value; } const value_type& value() const { return value_; } private
class Name
{
public:
typedef int32_t value_type;
public:
// ctors and dtors
void set_value(value_type value) { value_ = value; }
const value_type& value() const { return value_; }
private:
value_type value_;
};
class NamedObject
{
public:
void set_name(const Name& name) { name_ = name; }
const Name& name() const { return name_; }
protected:
// ctors and dtors
private:
Name name_;
};
我使用此类基类为对象提供具有预定义非虚拟功能的属性:
class MyObject: public NamedObject, public HasZlevel {
// functionality that is not connected with NamedObject and HasZLevel
};
因此我决定将MyObject视为“is-a”NamedObject,而不是“has-a”名称。
Z-level和Name是在MyObject实例的生存期内永远不会更改的属性。与聚合相比,我更喜欢这种方式,因为在仅为NamedObject或具有HasZLevel接口的对象定义的算法中使用了简化,我可以通过NamedObject*或HasZLevel*传递它们,并确保它们不会由于受保护的DTOR和CTOR而被删除或追加
另一个选项是聚合:
class MyObject
{
public:
MyObject(const Name& name): name_(name) {}
void set_name(const Name& name) { named_->set_name(name) }
// and so on...
private:
Name name_;
};
要使用这个,我需要一个模板算法,该算法要求from参数类型具有set_name成员函数
在我的情况下,有什么好的理由放弃我的mixin式设计并使用聚合吗?可能在长期维护和修改中?因为
这就是我喜欢管道胶带的原因
程序员。有时候,你在一个
团队,而你正忙于完成任务
密码,然后有人来找你
桌子,手里拿着咖啡杯,然后开始
喋喋不休地说如果你使用
多线程COM公寓,您的
该应用程序将有34%的亮点,而且
没那么难,因为他
写了一堆模板,所有的
你要做的就是继承
从他的17个模板中,每个模板
平均4个论点,而你
几乎不需要写下
功能。它只是一个巨大的
来自的多重继承列表
不同的班级,嘿,普雷斯托,
多单元线程COM。还有你的
眼睛在游动,你没有眼睛
他妈的知道这该死的东西是什么
我在说,但他就是不去
走开,即使他真的走了,
他正要回办公室
写更多他聪明的课程
完全由多个
从模板继承,不使用
单一的执行机构,以及
它会像疯了一样崩溃
你会在晚上被传呼到
进来试试看
因为他会在某个该死的地方
“设计模式”会议
管道磁带程序员不怕说,“多重继承太糟糕了,停下来,停下来。”
长话短说就是:这真的能帮助你更快地发布你的代码吗?或者,当某个被重写的成员函数阻塞继承树上的某个其他成员时,您将调试多个继承类,并在调试器中单步执行一个复杂的超类节点,这会花费更多的时间吗
是的,我确实写了“被一个会员噎住了”。多重继承真的很糟糕。问题是,如果你像在这里一样公开继承,在某个时候、某个地方,有人会将你的类视为
命名对象
或HasZlevel
,并尝试通过父指针删除、传递等等。这可能不会太奏效
如果您想使用这样的mixin类型策略,至少要使用private
继承,然后使用所需的父方法进入相应子类的公共部分
然而,我不会特别建议这样做。如果使用继承表示替换,则基类表示接口,而不是功能。我想说在这种情况下使用构图更有意义。整个标准库都是基于通用(模板)算法的,所以我不会去做这方面的工作。独立模板算法方法是一种扩展代码的好方法,无需修改类内部,甚至其公共接口。多重继承的主要问题是,有人试图将一个人建模为从“手臂”和“腿”继承,然后抱怨多重继承混淆了问题
Mixin来自LISP世界,我从来没有相信他们适合C++所有这一切。然而,多重继承本身并不是坏事,而且是必要的。一件事与不止一件事有“是-是”关系是很常见的
根据经验,我尝试只使用空基类,即Java调用的接口。如果我在基类中需要任何类型的数据,我几乎总是发现我的设计就像试图定义一个person类继承自“arm”和“leg”一样糟糕。也有例外,如果你认为你已经找到了,试着写一个文档,并请几个朋友对其进行评论。经验法则不是正式的数学证明,但它对我非常有用。经验法则是“尽可能使用最弱的关系”。看一看:和
您可以使用私有继承来建模“以术语实现”,但您应该考虑异常安全含义
我知道,我喜欢本周的大师
编辑
我使用这种基类来
为具有属性的对象提供
预定义的非虚拟功能
为什么聚合比继承更好:
1) 您可以使用继承隐藏方法(或更改名称)。默认情况下,所有方法都可用
2) 您可以使用不带虚拟析构函数的类(std::string和所有stl容器)
3) 您可以轻松地更改实现,如果您想要更改底层对象,那么您的接口将不会受到影响,但是如果您从不同的基类继承,那么您的接口可能会更改
4) 异常安全(参见第三个链接)
5) 容易阅读
当需要“私有继承”时:
1) 当要使用的类是抽象类时(不能使用聚合)
2) 当您想要使用受保护的方法时
当“公共继承”是
template<class T>
class Named: public T {
// all for named object
};
template<class T>
struct NamedCompare {
bool operator<const Named<T>* obj1, const Named<T>* obj2) {
//...
}
};
template<class T>
class NamedSet {
typedef std::set<NamedSet<Named<T>*, NamedCompare> type;
};
bool DoSmthWithFoo(Param1 p1,..., NamedSet<Foo>::type& set);
typedef Named<Featured<Blabla<Foo> > > NamedFeaturedBlablaFoo;
class NamedFeaturedBlablaFoo: public Named<Featured<Blabla<Foo> > > {};