使用类型转换在 unique_ptr 中创建对对象的引用

Posted

技术标签:

【中文标题】使用类型转换在 unique_ptr 中创建对对象的引用【英文标题】:Create reference to object in unique_ptr with typecast 【发布时间】:2017-12-06 09:48:52 【问题描述】:

我发现我仍然用 c 风格做了大量令人尴尬的事情,所以我现在正试图通过减少对原始指针的使用来拥抱新千年。我有一个unique_ptr<BaseClass> 的向量,每个向量都指向派生类的一个对象。我试图找到一种很好的方法来引用派生类的一个对象。目前,我通过使用.get() 函数并将其转换为派生类来做到这一点。

但是,据我了解,.get() 主要用于与坚持使用原始指针的遗留代码交互,应该避免使用它。如果可以避免的话,在unique_ptr 中有另一个指向对象的指针似乎不是很好的风格。有没有办法在不使用 get 的情况下获取对派生类对象的引用?或者其他一些方便的处理对象的方法?

这是一个简化的代码示例:

#include <iostream>
#include <vector>

class Fruit 
public:
    double size = 0;
    virtual ~Fruit() = default;
;

class Apple : public Fruit 
public:
    bool hasLeaf = false;
;

void doAppleStuff(std::vector<std::unique_ptr<Fruit> > &apples) 

    // assume we know that element [0] exists and it is definitely of type Apple
    auto apple = static_cast<Apple *> (apples[0].get());  // is there a better option?

    if (apple->hasLeaf) 
        std::cout << "We can leaf now" << std::endl;
     else 
        std::cout << "We are pitiably leafless" << std::endl;
    


int main() 
    std::vector<std::unique_ptr<Fruit> > fruitVec;
    auto apple = new Apple;
    apple->hasLeaf = true;
    std::unique_ptr<Fruit> applePt(apple);
    fruitVec.push_back(std::move(applePt));
    doAppleStuff(fruitVec);
    return 0;

(我认为在c++14中用make_unique缩短主函数是可能的)。

做这样的事情会是好的编码风格吗?

auto &apple = *static_cast<Apple *> (apples[0].get());

"Downcasting" unique_ptr<Base> to unique_ptr<Derived> 的答案概述了一种通过释放unique_ptr 并将新的unique_ptr 重新创建到派生类来“转换”unique_ptr 的方法,但这在这里似乎并不适用,因为我真的不想弄乱唯一指针的向量(除非我遗漏了什么)。

【问题讨论】:

由于Fruit没有虚拟析构函数,你的代码目前是UB。 如果你坚持使用指向基类的指针来存储类,那么你需要声明一些虚方法(尤其是析构函数)并调用它们而不是向上转换。 @Jarod42 好点...我将编辑代码以添加虚拟析构函数 为避免在基类中添加过多的虚方法,可以使用Visitor_pattern。 我去过那里;正如@Jarod42 所说,您可以检查虚拟方法和/或访问者是否有帮助。 CRTP 通常也是需要研究的东西。也在这里阅读***.com/questions/11002641/…,了解为什么动态转换唯一指针可能很复杂。 【参考方案1】:

如果你知道指针不是nullptr,一个更简单的转换是:

auto& apple = static_cast<Apple&>(*apples[0]);

此语法适用于所有支持T&amp; operator*() const 的智能指针(例如std::unique_ptrstd::shared_ptrboost::intrusive_ptr)。

在多重继承的情况下,from-base-reference-to-derived-reference 比转换 from-base-pointer-to-derived-pointer 更快,因为后者必须始终检查nullptr,然后再添加或减去指针的偏移量(以便nullptr 在转换后变成nullptr),而引用不能是nullptr,因此不需要运行时检查.

【讨论】:

【参考方案2】:

更安全的替代方法是定义辅助函数

template <typename T> // 
std::vector<T *> of_type(const std::vector<std::unique_ptr<Fruit>> & fruits) 
    std::vector<T *> result;
    for (auto & ptr : fruits) 
        if (T * item = dynamic_cast<T *>(ptr.get())) 
            result.push_back(item);
                    
    
    return result;

然后将doAppleStuff改为

void doAppleStuff(std::vector<Apple *> & apples);

称为

doAppleStuff(as_type<Apple>(fruitVec));

【讨论】:

以上是关于使用类型转换在 unique_ptr 中创建对对象的引用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ActiveRecords 中创建对 Ruby 中对象的引用?

如何在数据库项目中创建对动态数据库名称的引用?

在Firestore中创建对Cloud Storage文档的引用

如何在 Laravel 中创建对 db 的查询?

在 C++ 中创建对三元运算符结果的 const 引用是不是安全?

无法在 XAML 中创建对其他项目的 xmlns 引用