我对C++11的智能指针还很陌生,我正在努力在项目中有效地使用它们。在我的项目中,我有很多函数对unique_ptr
的vector
进行常量引用,对其进行一些计算,并将一些结果放在返回参数中,如下所示:
void computeCoefficients(const vector<unique_ptr<Scalar>>& roots,
vector<unique_ptr<Scalar>>& coeffs) {
...
}
我使用unique_ptr
是因为调用所有这些函数的过程是vector
中对象的唯一所有者,而这些函数只是"借用"对象,以便将它们作为输入读取。
现在,我正试图编写一个函数,对它接收的vector
的不同子集进行计算,为了做到这一点,它需要有包含这些子集的vector
的不同"版本",以便传递给另一个以vector<unique_ptr<Scalar>>
为输入的函数。但获得向量子集的唯一方法是复制它——这是一个问题,因为unique_ptr
s无法复制。我希望代码看起来像这样:
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));
}
}
当然那不管用。如果我用shared_ptr
s替换unique_ptr
s,我可以让它工作,但这有两个问题:
- 从哲学上讲,这意味着我与
computeOnSubsets
函数共享集合的所有权,而我不是;呼叫方仍然是唯一所有者。(我读到shared_ptr
意味着你与所有拥有它副本的东西共享所有权( - 它会引入引用计数的开销,即使在我不需要它的地方也是如此,因为它会迫使我将所有方法的输入参数更改为
vector<shared_ptr<Scalar>>
我只想制作一个指针的临时只读副本,唯一的目的是制作暂时只读
我使用unique_ptr是因为调用所有这些函数的过程是向量中对象的唯一所有者,而这些函数只是"借用"对象,以便将它们作为输入读取。
由于计算函数不拥有指向对象,只是观察它们的状态并进行计算,因此应该向它们传递观察指针的向量(在这种情况下,是常规原始指针(,而不是unique_ptr
s的向量。
由于computeOnAllSubsets()
和computeOnSet()
不对Scalar
对象的生存期负责,它们甚至不应该获得其所有权——换句话说,它们不应该接收到拥有的unique_ptr
s。
毕竟,程序的逻辑保证了这些函数不会接收悬空引用,因为拥有它的函数在执行所有必要的计算之前不会破坏它的向量。这是由你正在写的内容直接支持的:
我只想制作一个指针的临时只读副本,唯一的目的是制作临时只读子向量。有办法做到这一点吗?
只需将一个原始指针向量传递给您的计算函数。给定一个unique_ptr
,您可以通过调用成员函数get()
:来访问封装的原始指针
std::unique_ptr<Scalar> pS // ... initialized somehow
Scalar* pScalar = pS.get();
作为原始指针的替代方案,可以使用std::reference_wrapper
来调度观测引用。特别是在重构使用原始指针进行手动内存管理的遗留代码库的过程中,这将清楚地表明引用对象的所有权属于其他地方。
然而,请注意,在现代C++中,原始指针通常是观察指针的同义词,因此上述区别在广义上下文中并不真正有意义。std::reference_wrapper
是最基本的用例,当您希望通过引用某个函数模板来传递对象时,该模板按值接受其参数(std::bind()
是一个典型的示例(。
使用迭代器可能会使代码更加通用。只需声明您的计算函数如下:
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_each
和std::transform
。
我并不真正理解computeOnAllSubsets
中的代码片段,但我本能地认为std::transform
可能会有所帮助。