【问题标题】:Polymorphism : raw pointer vs smart pointer多态性:原始指针与智能指针
【发布时间】:2021-10-16 12:24:32
【问题描述】:

如何做到这一点?

我想使用多种类型(研究、添加、删除)的向量进行库存管理(药水、武器等。所有这些都派生自虚拟类 Item)。

我在这里简化了问题: 我有一个包含项目(基类)和武器(派生类)的向量。 对于内存管理问题,我更喜欢使用 unique_ptr 但没有办法返回或正确使用它。

下面的例子:

// UniquePointerAndPolymorphism.cpp : Ce fichier contient la fonction 'main'. L'exécution du programme commence et se termine à cet endroit.
//

#include <iostream>
#include <vector>

class Item
{
protected:
    std::string name_;
public:
    Item(std::string name) :name_(name) {};
    virtual std::string getName() { return "b_" + name_; };
};


class Weapon : public Item
{
public:
    Weapon(std::string name) : Item(name) {};
    std::string getName() override { return "d_" + name_; };
};

std::vector<std::unique_ptr<Item>> elements;

std::unique_ptr<Weapon> getAnElement_uniquePointer(int i)
{
    /*
     *
     * How to return unique pointer from the vector ?????
     *
     */
    return std::make_unique<Weapon>("returned");

}

Weapon* getAnElement_rawPointer(int i)
{
    if (auto oneElement = dynamic_cast<Weapon*>(elements[i].get()))
    {
        return oneElement;
    }
    else
    {
        return nullptr;
    }
}


int main()
{

    elements.push_back(std::make_unique<Weapon>("1"));
    elements.push_back(std::make_unique<Item>("2"));
    elements.push_back(std::make_unique<Weapon>("3"));

    Weapon* rptElement = getAnElement_rawPointer(2);
    std::cout << rptElement->getName() << std::endl;

    std::unique_ptr<Weapon> uptElement = std::move(getAnElement_uniquePointer(0));
    std::cout << uptElement->getName() << std::endl;

}

所以我有几个问题:

  • 即使看起来不是问题,多态是否兼容 使用智能指针?
  • 返回原始指针是唯一的解决方案吗?
  • 是否必须使用 shared_pointer 才能在向量和程序的另一部分中使用它?
  • 有没有办法返回引用?

谢谢。 塞巴斯蒂安

【问题讨论】:

    标签: c++ polymorphism smart-pointers raw-pointer


    【解决方案1】:

    即使看起来不是问题,多态性是否与智能指针兼容?

    是的,多态性与智能指针兼容。

    返回原始指针是唯一的解决方案吗?

    不,返回原始指针不是唯一的解决方案。 (请参阅下面的可选参考答案。)

    一种约定是使用原始指针来指示非拥有指针

    不幸的是,通常不清楚 intent 是否将原始指针变为非拥有指针,因为历史上原始指针通常是拥有指针。

    如果您采用这样的策略,您将需要遵守原始指针是非拥有指针的规则。 (我认为这是一个很好的政策,我使用它。)

    我是否必须使用 shared_pointer 才能在向量和程序的另一部分中使用它?

    不,您没有使用 shared_ptr。使用 shared_ptr 是联合所有权。

    联合所有权相当于是一个全局变量。除非对象是不可变的,否则对对象状态的推理是很棘手的,尤其是当不同的所有者有不同的不变量时。

    我考虑使用 shared_ptr 作为最后的手段。我使用的智能指针是一个 unique_ptr(原始指针是非拥有的)。

    有没有办法返回引用?

    是的,您可以返回对武器的引用。

    Weapon& getAnElement_reference(int i) {
        if (auto oneElement = dynamic_cast<Weapon*>(elements[i].get()))
        {
            return *oneElement;
        }
    
        throw std::logic_error("No weapon");
    }
    

    但是如果索引引用的对象不是武器怎么办?代码需要抛出异常。

    抛出异常的另一种方法是返回std::optional&lt;std::reference_wrapper&lt;Weapon&gt;&gt;,当该索引处没有武器时,它可能是std::nullopt_t。然后调用者需要显式访问可选。

    如果调用者忽略检查nullptr,这也比原始指针更安全,这是一个容易犯的错误,编译器不会帮助捕捉这些错误。

    【讨论】:

    • 很好的答案,非常感谢,我更好地了解了唯一指针的情况。我故意删除了一些检查以简化我的帖子。最后,我没有比原始指针更好的解决方案。输出对 Weapon 的引用不适用于多态性(因为我在代码中将武器进一步分叉到 Sword 和 Bow 中)
    【解决方案2】:

    唯一的指针就是:唯一的。每个对象只应该有一个唯一的指针。一旦您尝试返回唯一指针,就必须有 2 个:一个在您的向量元素中,一个从您的函数返回。我认为有一些方法可以解决这个问题,比如返回对指针的引用,但这很快就会变得混乱。

    你可以从一个 unique_ptr 创建一个 shared_ptr,或者你可以在你的向量中使用一个 shared_ptr 来开始。

    或者,在库存系统的情况下,您使用的是相对简单的对象。根据我的经验,您需要一个用于实际游戏世界中的物品的类,并为存储在库存中的物品提供一个单独的 InventoryItem 类。

    然后每个项目将存储它构造的inventoryItem 的类型。这不仅意味着您不必为确保库存物品不会出现在您的游戏世界中而费尽心思,还意味着如果更方便的话,您可以轻松地按价值传递这些物品,因为数量这样一个项目中的实际数据小得可笑:)

    归根结底,如何管理指针,或者是否应该使用指针,真的是个人选择。根据此处的用途,只有 unique_ptr 不会被使用。

    【讨论】:

    • 您好,感谢您的回复。我完全同意世界库存与玩家库存。在这种情况下,我更多的是关于“如何从我的库存中装备一些东西”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2016-08-01
    • 1970-01-01
    • 2019-06-30
    • 2013-05-10
    • 2013-05-07
    • 1970-01-01
    相关资源
    最近更新 更多