Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 构图模式_C++_Design Patterns_Inheritance_Encapsulation_Composition - Fatal编程技术网

C++ 构图模式

C++ 构图模式,c++,design-patterns,inheritance,encapsulation,composition,C++,Design Patterns,Inheritance,Encapsulation,Composition,如何处理组合而不是继承?考虑下面的类: class GameObject {...}; class Sprite { public: void changeImage(...); }; class VisibleGameObject: public Sprite, public GameObject {}; class VisibleGameObject : public GameObject { protected: Sprite m_sprite; };

如何处理组合而不是继承?考虑下面的类:

class GameObject {...};

class Sprite {
public:
    void changeImage(...);
};

class VisibleGameObject: public Sprite, public GameObject {};

class VisibleGameObject : public GameObject {
    protected:
        Sprite m_sprite;
};
第一个VisibleGameObject类使用继承。多重继承。看起来不太好。第二个是我想使用的,但它不允许我像这样访问Sprite的API:

VisibleGameObject man;
man.changeImage();
没有继承(或代码复制)如何实现这一点

编辑:
我知道我可以只使用继承或使
m_sprite
成为公共成员,而我不能访问sprite类,因为它是
私有的。这就是问题所在,问题是关于按照数据封装规则更改
VisibleGameObject
精灵的最佳方法。

看起来您正在尝试直接访问精灵的功能,而不首先引用它。试试这个:

man.m_sprite.changeImage() ;

请注意,m_sprite和changeImage()应该是公共的。否则,请使用公共访问器函数来操作私有类成员。

我认为您仍然需要“组合优先于继承”的思维方式。基类应该知道要合成什么。若要更改图像,您应该更改sprite实例,您不应该提供组合实例的接口。例如:

class GameObject {
public:
    // you can choose public access or getters and setters, it's your choice
    Sprite sprite;
    PhysicalBody body;
};

object = GameObject();
object.sprite = graphicalSystem.getSpriteFromImage("image.png");
// or if you prefer setters and getters
object.setSprite(sprite);
class Component {
    // ...
};

class Sprite : public Component {
    //...
};

class PhysicalBody : public Component {
    //...
};

class GameObject {
protected:
    std::map<std::string, Component*> components;
    //...
public:
    Component* getComponent(const std::string& name) const;
    void setComponent(const std::string& name, Component* component);
    //...
};
更一般地说,游戏对象应该包含基类组件的实例(或指向实例的指针,具体取决于您的实现)。在这种情况下使用继承是有意义的,因为这样它们就可以放在一个存储中,比如std::map。例如:

class GameObject {
public:
    // you can choose public access or getters and setters, it's your choice
    Sprite sprite;
    PhysicalBody body;
};

object = GameObject();
object.sprite = graphicalSystem.getSpriteFromImage("image.png");
// or if you prefer setters and getters
object.setSprite(sprite);
class Component {
    // ...
};

class Sprite : public Component {
    //...
};

class PhysicalBody : public Component {
    //...
};

class GameObject {
protected:
    std::map<std::string, Component*> components;
    //...
public:
    Component* getComponent(const std::string& name) const;
    void setComponent(const std::string& name, Component* component);
    //...
};
类组件{
// ...
};
类Sprite:公共组件{
//...
};
类PhysicalBody:公共组件{
//...
};
类游戏对象{
受保护的:
地图组件;
//...
公众:
Component*getComponent(const std::string&name)const;
void setComponent(const std::string&name,Component*Component);
//...
};
对于主循环中的组件创建和渲染,请使用系统。例如,GraphicalSystem知道它创建的精灵的所有实例,在渲染时,它只渲染附加到某个游戏对象实例的精灵。可以对分离的组件进行垃圾回收。有关位置和大小的信息可能是游戏对象的一部分,也可能是“物理”组件

理解它的最好方法是编写自己的原型或检查现有的实现(,以及其他许多实现)。有关更多信息,请参阅或尝试查找实体/组件系统。

首先,由于模型a和模型a之间都有-a关系,因此为实体/组件系统(而非公共系统)

重要的问题是我们如何将
Sprite
公共成员(例如
changeImage
)公开给
VisibleGameObject
客户端?我介绍了我知道的4种方法:

(私人)继承 我知道您希望避免(多重)继承,但为了完整性,我基于私有继承提出了一个建议:

class VisibleGameObject: private Sprite, public GameObject {
...
};
在这种情况下,
VisibleGameObject
私下派生自
Sprite
。那么前者的用户就不能访问后者的任何成员(就好像它是一个私有成员一样)。特别是,
Sprite
的公共和受保护成员对
VisibleGameObject
客户端隐藏

如果继承是公开的,那么所有
Sprite
的公开和受保护成员将由
VisibleGameObject
向其客户端公开。通过私有继承,我们可以更好地控制哪些方法应该通过声明公开。例如,这将公开
Sprite::changeImage

class VisibleGameObject1: private Sprite, public GameObject {
public:
    using Sprite::changeImage;
...
};
转发方法 我们可以给
VisibleGameObject
公共方法转发调用到
mu sprite
,如下所示

class VisibleGameObject2: public GameObject {
public:
    void changeImage() {
        m_sprite.changeImage();
    }
private:
    Sprite m_sprite;
...
};
我相信这是最好的设计,尤其是在封装方面。但是,与其他备选方案相比,它可能需要大量键入

结构解引用算子 即使是普通的旧C也提供了公开另一个类型的接口的类型,就好像它是自己的:指针

实际上,假设
p
属于
Sprite*
类型。然后,通过使用结构解除引用操作符
->
,我们可以访问
Sprite
(由
p
指向)的成员,如下所示

p->changeImage();
C++允许我们为类赋予定制的结构取消引用操作符(智能指针很好地使用了这一特性)。我们的例子是:

class VisibleGameObject3 : public GameObject {
public:
    Sprite* operator ->() {
        return &m_sprite;
}
private:
    Sprite m_sprite;
...
};

这种方法虽然方便,但有许多缺陷:

  • 至于公共继承,这种方法不能很好地控制哪些
    Sprite
    公共成员应该被公开
  • 它只对一个成员有效(也就是说,不能使用相同的技巧公开两个成员接口)
  • 它把界面弄乱了。事实上,例如,考虑<代码> VisualGalaseObjult/Cuth>有一个方法:代码> DOMMETHONSE()/<代码>。然后,要对对象调用此方法,应该执行
    v.doSomething()
    ,而要调用
    changeImage()
    应该使用
    v->changeImage()
    。这令人困惑
  • 它使
    VisibleGameInterface
    看起来像一个智能指针。这在语义上是错误的 C++11包装器模式 最后,还有Sutter的C++11包装模式(看看他的,特别是第9页的第二页):


    正如我们所看到的,与转发方法相比,这种方法将键入的负担从类编写器转移到类客户端。

    您无法访问Sprite这样的方法。只能通过Sprite的对象调用Sprite的方法。另外,您希望将changeImange()
    public
    ,以便派生类可以访问它们。我认为
    friend
    不会有太大变化。至于OP的问题,它不能。您要么求助于多重继承(这并不一定总是坏的),要么自己重新编写包装器方法。@Nbr44,我忽略了最后几行,因此我的评论不正确。对不起
    VisibleGameObject4 v4;
    v4( [](Sprite& s) { return s.changeImage(); } );