【问题标题】:getting a segfault when I try to deep copy `unique_ptr`s当我尝试深度复制`unique_ptr`s时出现段错误
【发布时间】:2021-06-27 23:00:49
【问题描述】:

为什么我不能从 clone 函数返回 unique_ptr?我以为I could do this

我有一个名为transform 的不同数学函数的基类。我有一个指向这种类型的指针容器,因为我使用的是多态性。例如,所有这些派生类都有不同的 log_jacobian 实现,这对统计算法很有用。

我在这个transform 类中使用unique_ptrs,所以我创建了一个(纯虚拟)clone 函数,它使新的unique_ptr 指向同一个数学transform 对象的深层副本.这个新对象与派生自 transform<float_t> 的类型相同,但它是独立的,因为不能有两个 unique_ptrs 指向同一事物。

template<typename float_t>
class transform{
...
virtual std::unique_ptr<transform<float_t>> clone() const = 0;
...
};

我的transform_container 班级一次拥有其中一些。毕竟,大多数统计模型都有不止一个参数。

template<typename float_t, size_t numelem>
class transform_container{
private:

    using array_ptrs = std::array<std::unique_ptr<transform<float_t>>, numelem>;
    array_ptrs m_ts;
    unsigned m_add_idx;
...
    auto get_transforms() const -> array_ptrs;
};

不过,我不确定为什么深层复制功能 get_transforms 不起作用。它用于制作副本,以及访问容器中的各个转换。运行一些测试时出现段错误。如果我在gdb 中运行它,它会明确地告诉我在错误之后带有注释的行。

template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
    array_ptrs deep_cpy;
    for(size_t i = 0; i < numelem; ++i){
        deep_cpy[i] = m_ts[i]->clone(); // this line
    }
    return deep_cpy;
}

我也尝试将std::move 输入deep_cpy[i] 并使用unique_ptr::reset,但无济于事。

编辑:

这里有一些其他相关的方法:一个为transform_container添加转换的方法,以及为个人transform添加的工厂方法:

template<typename float_t>
std::unique_ptr<transform<float_t> > transform<float_t>::create(trans_type tt)
{
    if(tt == trans_type::TT_null){
        
        return std::unique_ptr<transform<float_t> >(new null_trans<float_t> );
    
    }else if(tt == trans_type::TT_twice_fisher){
        
        return std::unique_ptr<transform<float_t> >(new twice_fisher_trans<float_t> );
    
    }else if(tt == trans_type::TT_logit){
        
        return std::unique_ptr<transform<float_t> >(new logit_trans<float_t> );
    
    }else if(tt == trans_type::TT_log){

        return std::unique_ptr<transform<float_t> >(new log_trans<float_t> );
    
    }else{

        throw std::invalid_argument("that transform type was not accounted for");
    
    }
}

template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
    m_ts[m_add_idx] = transform<float_t>::create(tt);
    m_add_idx++;
}

【问题讨论】:

  • numelem 数组中的所有numelem 元素都有效吗? IE。是否所有指针都已初始化并指向实际的transform&lt;float_t&gt; 对象?
  • minimal reproducible example 会很有帮助。我写了一个快速的玩具程序,它运行没有问题。
  • 在有问题的行之前添加if (!m_ts[i]) throw "oopsies";。摆脱偏执狂。
  • @Taylor:是的,你不能告诉一个不存在的东西来克隆它自己
  • 另外,学习如何使用调试器;您将能够在崩溃时停止并检查变量。

标签: c++ c++11 unique-ptr move-semantics


【解决方案1】:

get_transforms() 中,您正在循环整个 m_ts[] 数组,在所有 元素上调用clone() - 甚至那些未被@ 分配的元素987654325@呢!未分配的unique_ptrs 将持有一个nullptr 指针,它是undefined behavior to call a non-static class method through a nullptr

最简单的解决方法是将get_transforms() 中的循环更改为使用m_add_idx 而不是numelem

template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
    array_ptrs deep_cpy;
    for(size_t i = 0; i < m_add_idx; ++i){ // <-- here
        deep_cpy[i] = m_ts[i]->clone();
    }
    return deep_cpy;
}

否则,您将不得不手动忽略任何 nullptr 元素,例如:

template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
    array_ptrs deep_cpy;
    for(size_t i = 0, j = 0; i < numelem; ++i){
        if (m_ts[i]) {
            deep_cpy[j++] = m_ts[i]->clone();
        }
    }
    return deep_cpy;
}

无论哪种方式,您都应该更新add_transform() 以验证m_add_idx 不会超过numelem

template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
    if (m_add_idx >= numelem) throw std::length_error("cant add any more transforms"); // <-- here
    m_ts[m_add_idx] = transform<float_t>::create(tt);
    ++m_add_idx;
}

话虽如此,由于transform_container 可以分配可变数量的转换,我建议将transform_container 更改为使用std::vector 而不是std::array,例如:

template<typename float_t>
class transform_container{
private:

    using vector_ptrs = std::vector<std::unique_ptr<transform<float_t>>>;
    vector_ptrs m_ts;
...
    auto get_transforms() const -> vector_ptrs;
};

template<typename float_t>
auto transform_container<float_t>::get_transforms() const -> vector_ptrs
{
    vector_ptrs deep_cpy;
    deep_cpy.reserve(m_ts.size());
    for(const auto &elem : m_ts){
        deep_cpy.push_back(elem->clone());
    }
    return deep_cpy;
}

template<typename float_t>
std::unique_ptr<transform<float_t>> transform<float_t>::create(trans_type tt)
{
    switch (tt) {
        case trans_type::TT_null:
            return std::make_unique<null_trans<float_t>>();

        case trans_type::TT_twice_fisher:
            return std::make_unique<twice_fisher_trans<float_t>>();
    
        case trans_type::TT_logit:
            return std::make_unique<logit_trans<float_t>>();
    
        case trans_type::TT_log:
            return std::make_unique<log_trans<float_t>>();
    }    

    throw std::invalid_argument("that transform type was not accounted for");
}

template<typename float_t>
void transform_container<float_t>::add_transform(trans_type tt)
{
    m_ts.push_back(transform<float_t>::create(tt));
}

【讨论】:

    猜你喜欢
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    相关资源
    最近更新 更多