C++ 在C++;,当对象关系为';这不是直觉吗?

C++ 在C++;,当对象关系为';这不是直觉吗?,c++,inheritance,composition,code-reuse,C++,Inheritance,Composition,Code Reuse,如果这有点模糊,我很抱歉,但我不知道在以下情况下如何进行代码重用。我用的是C++。 该程序是一个简单的模拟,它有一些不同的东西在发挥作用 struct StupidBug; struct SmartBug; struct Plant; struct Mammal; 这些东西中的每一个都有一套他们能够做到的事情 struct StupidBug { // Can walk, // Can eat, // Can see surroundings, // Can s

如果这有点模糊,我很抱歉,但我不知道在以下情况下如何进行代码重用。我用的是C++。 该程序是一个简单的模拟,它有一些不同的东西在发挥作用

struct StupidBug;
struct SmartBug;
struct Plant;
struct Mammal;
这些东西中的每一个都有一套他们能够做到的事情

struct StupidBug
{
    // Can walk,
    // Can eat,
    // Can see surroundings,
    // Can spawn,
};

struct SmartBug
{
    // Can walk,
    // Can eat,
    // Can see surroundings,
    // Can see extended surroundings,
    // Can spawn,
};

struct Plant
{
    // Can spawn
    // Can see surroundings
};

struct Mammal
{
    // Can walk,
    // Can eat,
    // Can see surroundings,
    // Can see extended surroundings,
};
这些生命形式之间的功能有相当多的重叠,但我不知道如何在它们之间共享代码。我想不出任何适合所有这些的继承方式。我试着写作,做了一些如下的事情:

struct AbilityToWalk
{
    void walk(position, direction)
    {
        // walk code
    }
};

struct StupidBug
{
    AbilityToWalk walk;
    // other abilities
};

struct SmartBug
{
    AbilityToWalk walk;
    // other abilities
};

// etc...
但是walk函数依赖于调用它的对象的位置。将生命形式的位置传递给它自己的成员函数感觉很奇怪,总体上感觉是一个非常笨拙的解决方案


我不知道如何着手处理这种情况。有没有一种方法能做到这一点,既直观又优雅?我遗漏了一些非常明显的东西吗?

您可以实现如下的mixin系统:

struct Walker
{
    virtual void walk();    
};
struct Spawner
{
    virtual void spawn();   
};
struct Seer
{
    //seeing extended surroundings could be a specialized version of this
    virtual void see(); 
};
struct Eater
{
    virtual void eat(); 
};

struct Plant : Spawner, Seer
{
    //inherit default see function
    void spawn() override; //override spawn function
};
通过这种方式,您可以实现代码重用和接口的细粒度,从而可以执行以下操作:

std::vector<std::unique_ptr<Walkers>> walkers;
walkers.push_back(std::make_unique<Mammal>());
//...
for (auto&& walker : walkers) walker->walk();
std::矢量步行机;
向后推(std::make_unique());
//...
对于(自动和步行者:步行者)步行者->步行();
我并不是说组合比继承更受欢迎

我只是想回答这个问题的一部分:

但是walk函数依赖于调用的对象的位置 信息技术把一个生命体的位置传给它自己的成员感觉很奇怪 功能,总体上感觉是一个非常笨拙的解决方案


我添加这个答案主要是为了对解决方案进行完整的解释,以及其他发现类似问题的人

我采用了塔尔坦拉玛的类似混合系统的方法。每种能力都有自己的职业,生命形式继承了它所需要的所有能力

然而,仍然存在一个问题,即这些能力需要能够访问生命形式中包含的某些价值,例如它的位置

class Mover
{
public:

    virtual void move(Vec2 direction)
    {
                   /*the problem*/
        Vec2 newPos = position + direction;
        position = newPos;
    }
};


class DumbBug : public Mover
{
public:
    /*the problem*/
    Vec2 position;

    DumbBug(Vec2 pos)
        : position(pos)
    {}
};
按照用户3528438的建议,在mixin中创建一个引用变量并将其设置为构造函数中生命形式的位置是不可行的,这会造成混乱的行为

事实证明,这个问题的解决方案非常简单:纯虚拟getter/setter

class Mover
{
public:
    virtual void move(Vec2 direction)
    {
                     /*the solution*/
        Vec2 newPos = get_position() + direction;
        set_position(newPos);
    }

    /*--- the solution -------------------*/
    virtual Vec2 get_position() = 0;
    virtual void set_position(Vec2 pos) = 0;
};


class DumbBug : public Mover
{
private:
    Vec2 position;

public:
    DumbBug(Vec2 pos)
        : position(pos)
    {}

    /*- the solution -------------------*/
    virtual Vec2 get_position() {
        return position;
    }
    virtual void set_position(Vec2 pos) {
        position = pos;
    }

};
纯虚拟函数迫使您在生命形式类中定义它们,允许mixin访问它需要的内容,并且可以在许多不同的生命形式上重用

我认为这个解决方案足够直观和可重用,因此我将继续使用它


我意识到这个问题有点含糊不清,对此表示歉意,但还是要感谢大家的帮助。

多父母继承?父母是不同的能力?混合?超载?仿制药?完成大部分工作的助手函数?建模如何,比如“is-a Walker”这是关于代码重用还是关于多态性使用?如何将植物添加到Walker向量中?在您的实现中,plant没有实现Walker接口。如果除
walk
之外的任何成员需要访问location,location需要得到保护,这确实很难闻。我尝试了这个方法,发现每个StupidBug实例的位置都是一样的。屏幕上有三个人,分别处于不同的位置,但他们每个行走能力的构成是相同的。这只会导致最后一个来回走动。@djscrew如果
mPosition
s是相同的,它们怎么可能是不同的位置呢?这正是问题所在。每个虫子的位置不会和每个步行者的位置对齐。很难用语言来解释,它显示了当前的实现,并且更清楚地解释了问题。粘贴使用了另一个答案中的mixin实现,但奇怪的结果与我在这个答案中设置实现时相同。@djscrew您的
DumbBug
mPosition
覆盖了基类
Walker
中的
mPosition
,这使得它没有初始化。你很幸运没有使程序崩溃。我该如何着手解决这个问题?如果我从Walker中删除mPosition,它将不会编译,如果我不将mPosition声明为引用,它将出错。
class Mover
{
public:
    virtual void move(Vec2 direction)
    {
                     /*the solution*/
        Vec2 newPos = get_position() + direction;
        set_position(newPos);
    }

    /*--- the solution -------------------*/
    virtual Vec2 get_position() = 0;
    virtual void set_position(Vec2 pos) = 0;
};


class DumbBug : public Mover
{
private:
    Vec2 position;

public:
    DumbBug(Vec2 pos)
        : position(pos)
    {}

    /*- the solution -------------------*/
    virtual Vec2 get_position() {
        return position;
    }
    virtual void set_position(Vec2 pos) {
        position = pos;
    }

};