12

我对 C++11 的智能指针很陌生,我正在尝试在项目中有效地使用它们。在我的项目中,我有很多函数接受对 a vectorof的 const 引用unique_ptr,对其进行一些计算,并将一些结果放在返回参数中,如下所示:

void computeCoefficients(const vector<unique_ptr<Scalar>>& roots, 
    vector<unique_ptr<Scalar>>& coeffs) {
    ...
}

我正在使用unique_ptr,因为调用所有这些函数的过程是 中对象的唯一所有者vector,并且这些函数只是“借用”对象以便将它们作为输入读取。

现在我正在尝试编写一个vector对它接收的不同子集进行计算的函数,为了做到这一点,它需要具有vector包含这些子集的不同“版本”,以便传递给另一个需要vector<unique_ptr<Scalar>>作为输入。但是获得向量子集的唯一方法是复制它——这是一个问题,因为unique_ptrs 不能被复制。我希望代码看起来像这样:

void computeOnSet(const vector<unique_ptr<Scalar>>& set, unique_ptr<Scalar>& output) {  
    ...
}

void computeOnAllSubsets(const vector<unique_ptr<Scalar>>& set, vector<unique_ptr<Scalar>>& outputs) {
    for(int i = 0; i < input.size(); i++) {
        auto subset = vector<unique_ptr<Scalar>>(set.begin(), set.begin()+i);
        subset.insert(subset.end(), set.begin()+i+1, set.end();
        computeOnSubset(subset, outputs.at(i));
    }
}

当然,这是行不通的。unique_ptr如果我用s替换 s,我可以让它工作shared_ptr,但这有两个问题:

  • 从哲学上讲,这意味着我与computeOnSubsets函数共享集合的所有权,而我不是;调用者仍然是唯一的所有者。(我读到这shared_ptr意味着您与所有拥有它的副本的东西共享所有权)。
  • 即使在我不需要它的地方,它也会引入引用计数的开销,因为它会迫使我将所有方法的输入参数更改为vector<shared_ptr<Scalar>>.

我要做的就是制作一个临时的、只读的指针副本,其唯一目的是制作临时的、只读的子向量。有没有办法做到这一点?weak_ptr听起来像我需要的(非拥有临时指针),但它只能与shared_ptr.

4

2 回答 2

11

我使用 unique_ptr 因为调用所有这些函数的过程是向量中对象的唯一所有者,并且这些函数只是“借用”对象以便将它们作为输入读取。

由于计算函数不拥有指向的对象,只是观察它们的状态并进行计算,因此您应该将观察指针的向量(在本例中为常规原始指针)传递给它们,而不是 s 的向量unique_ptr

因为computeOnAllSubsets()并且computeOnSet()不对对象的生命周期负责Scalar,所以它们甚至不应该获得它们的所有权——换句话说,它们不应该获得所有权unique_ptr

毕竟,程序的逻辑可以保证这些函数不会接收悬空引用,因为拥有函数在执行所有必要的计算之前不会破坏它的向量。您正在编写的内容直接支持这一点:

我要做的就是制作一个临时的、只读的指针副本,其唯一目的是制作临时的、只读的子向量。有没有办法做到这一点?

只需将原始指针向量传递给您的计算函数。给定 a unique_ptr,您可以通过调用成员函数来访问封装的原始指针get()

std::unique_ptr<Scalar> pS // ... initialized somehow
Scalar* pScalar = pS.get();

作为原始指针的替代方案,您可以std::reference_wrapper用于调度观察引用。特别是在重构遗留代码库的过程中,原始指针用于手动内存管理,这将清楚地表明引用对象的所有权属于其他地方。

但是请注意,在现代 C++ 中,原始指针通常是观察指针的同义词,因此上述区别在广义上下文中没有真正意义。最基本的用例std::reference_wrapper是当您希望通过引用某个函数模板来传递对象时,该模板按值接受其参数(std::bind()这是一个典型示例)。

于 2013-04-08T19:56:41.487 回答
2

使用迭代器可能会使您的代码更加通用。只需声明您的计算函数,如:

template <typename InputIt, typename OutputIt>
void compute(InputIt first_it, InputIt last_it, OutputIt d_first, Output d_last);

此计算函数模板处理范围内的数据[first_it, last_it),然后将结果放入[d_first, d_last). 它不关心输入或输出容器的类型。迭代器充当容器中元素的指针,这是 STL 的思想。

此外,在某些情况下,您甚至不需要手动检查范围。只需在 中使用函数模板<algorithm>,例如std::for_eachstd::transform

我不太了解 中的代码片段computeOnAllSubsets,但本能地我认为这std::transform可能会有所帮助。

于 2013-04-09T09:09:29.167 回答