在遍历vector <shared_ptr <baseclass >>时能否以某种方式调用派生类方法?

时间:2018-11-17 00:27:56

标签: c++

我有以下问题:

class Component
{
public:
virtual void update(){};
};


class TestComponent : public Component
{
    void update()override;
};


class GameObject
{
public :
void addComponent(Component& comp)
{
 std::shared_ptr<Component> test = std::make_shared<Component>(comp);
 components.push_back(test);
}
void GameObject::update()
{
 for(auto comp : components)
 {
 //I want to call the derived update here without casting it to the derived class if possible
 comp->update();
 }
}
private:
    std::vector<std::shared_ptr<Component>> components;
};

Somewhere else in my code:

GameObject go;
TestComponent comp;
go.addComponent(comp);

我只是假设当我将一个对象添加到Components的向量中时,我可以简单地对所有vectors元素调用update,并且它使用传递给addComponent的对象的重写更新。因此,对于上面的示例,我希望forloop调用我添加的TestComponent的更新,而不是基类的更新。但这不是正在发生的事情,所以我认为我错过了一些东西。 也许我的方法总体上是错误的。我不太确定我为此使用了共享指针吗? 朝正确方向的任何提示将不胜感激。

2 个答案:

答案 0 :(得分:7)

向量中没有TestComponent个对象。它们都是Component

void addComponent(Component& comp)
{
    std::shared_ptr<Component> test = std::make_shared<Component>(comp);
    components.push_back(test);
}

在此函数中,您将创建一个新的Component对象,该对象是您传入的Component对象的TestComponent子对象的副本。被称为object slicing

您将需要避免复制对象或实现某种可克隆的接口。

为避免复制对象,可以执行以下操作:

class GameObject
{
public:
    void addComponent(std::shared_ptr<Component> comp)
    {
        components.push_back(comp);
    }
    // ...
};

int main() {
    GameObject go;
    std::shared_ptr<TestComponent> testComponent = std::make_shared<TestComponent>();
    go.addComponent(testComponent);
}

在这种情况下,maingo共享单个TestComponent对象的所有权。如果要避免这种情况,可以实现一个可克隆的接口,以便对象知道如何复制自己:

class Component
{
public:
    virtual void update(){};
    virtual std::shared_ptr<Component> clone() const
    {
        return std::make_shared<Component>(*this);
    }
};


class TestComponent : public Component
{
    void update() override;
    std::shared_ptr<Component> clone() const override
    {
        return std::make_shared<TestComponent>(*this);
    }
};

class GameObject
{
public:
    void addComponent(const Component& comp)
    {
        components.push_back(comp.clone());
    }
    // ...
};

int main()
{
    GameObject go;
    TestComponent comp;
    go.addComponent(comp);
}

在这种情况下,您仍然可以进行复制,但是每个类都必须重写clone方法。


关于shared_ptr的问题:std::shared_ptr是一个智能指针,它在多个所有者之间共享对象的所有权。一个或多个std::shared_ptr拥有的对象只有在共享它的所有std::shared_ptr对象的所有权都被破坏时才被破坏。如果您不需要这种行为,则std::unique_ptr存在,并且性能会更高。 std::unique_ptr为唯一所有权建模。一次只能有一个std::unique_ptr对象可以引用一个对象,而当该std::unique_ptr被销毁时,该对象也将被销毁。

在这种情况下,可以使用两种类型的智能指针:

  • 如果您希望std::shared_ptr能够与其他所有者(也许是其他GameObject)共享其组件的所有权,请使用GameObjects
  • 如果您希望std::unique_ptr拥有其组件的专有所有权,请使用GameObject。在这种情况下,GameObject仍然可以允许其他对象访问其组件,但是组件的生存期将与GameObject
  • 的生存期联系在一起。

答案 1 :(得分:1)

要使您的代码编译,只需添加另一种方法,其余都很好。由于update方法是虚拟的,并且基类不是抽象的,因此两者都可以调用update而不会出现任何问题。

void TestComponent::addComponent(const TestComponent & tcomp)
{
    std::shared_ptr<Component> test = std::make_shared<TestComponent >(tcomp);
    components.push_back(test);
}

编辑:要添加任何组件,派生类或基类,请使用以下方式:

void TestComponent::addComponent(std::shared_ptr<Component> comp)
{
    components.push_back(comp);
}