C++ - 在模板方法中将 <T> 对象添加到 std::vector<abstract C> 的正确方法,无需复制 T

C++ - Proper way to add<T> obiect to std::vector<abstract C> in template method without copy T

一个程序有一个名为 Component 的接口:

class Component
{
public:
    virtual void Start() = 0;
    virtual void Update() = 0;
};

该接口由MeshComponent实现:

.h

#include "Component.h"
#include "Mesh.h"

class MeshComponent: public Component
{
public:
    Mesh* mesh;
    int a = 0; // for test
    MeshComponent();
    
    void Start();
    void Update();
};

.cpp

#include "MeshComponent.h"

MeshComponent::MeshComponent(): mesh(nullptr)
{
}

void MeshComponent::Start()
{
    std::cout << this << " " << a << "Start Mesh component\n";
}

void MeshComponent::Update()
{}

并在对象中使用:

#include "Mesh.h"
#include "Vector3.h"
#include "Component.h"
class Object
{
private:
    void bindPosition();

    void updateComponents();

    std::map<std::string, std::vector<std::unique_ptr<Component>>> components;

public:
    Vector3 position;
    Object();
    ~Object();

    // T:Component
    template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
    void addComponent(T component) {
        Component* comp = ((Component*)&component);
        std::string name(typeid(T).name());
        std::vector<std::unique_ptr<Component>>& arr = components[name];
        arr.push_back(std::unique_ptr<Component>(comp));
        T& c = (T&)*arr.back();
        c.Start();
    }
};

如您所见,我使用 std::map> 来包含组件。这是因为我想使用这样的东西:

parent.getComponent<Movement>(); // first one
parent.getComponents<Material>(); // array

每个组件都必须是唯一的 - 这就是它被复制的原因,我希望用户知道它被复制了。我不想复制它两次所以我想到使用 unique_ptr.

我想在删除对象时避免使用析构函数。这就是我避免使用指针的原因。 我也想避免使用移动原理图。

如果你读过这段代码,你可能会看到我的错误,即 'T component' 在超出范围时被删除(我通过写这个问题发现它。这是这个问题的主要原因)。

请教各位大神,我应该怎样做才是正确的。通过查看它,我认为我做了可以避免的不必要的转换(使用抽象 class 是地狱)或者包含组件的方式是错误的。

我指望你了。

template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T component) {
    Component* comp = ((Component*)&component);
    // ...
    arr.push_back(std::unique_ptr<Component>(comp));
    // ...
}

是的,你不能那样做。正确的方法是将其复制到一个新的唯一指针中:

template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T component) {
    // ...
    arr.push_back(std::make_unique<T>(comp));
    // ...
}

正如你所说,虽然你不想要两个副本,所以你可以通过 const 引用来接受你的论点:

template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value>::type* = nullptr>
void addComponent(T const& component) {
    // ...
    arr.push_back(std::make_unique<T>(comp));
    // ...
}

如果您发送右值,您还可以添加重载来移动对象,尽可能使其成为零副本:

template<typename T, typename std::enable_if<std::is_base_of<Component, T>::value
                                          && !std::is_reference<T>::value>::type* = nullptr>
void addComponent(T&& component) {
    // ...
    arr.push_back(std::make_unique<T>(std::move(comp)));
    // ...
}