[英]Handling smart pointers in stl container
I've a class Foo<T>
which has a vector of smart pointers to Shape
derived classes. 我有一个类Foo<T>
,它有一个指向Shape
派生类的智能指针。 I'm trying to implement an at(index)
member function. 我正在尝试实现at(index)
成员函数。 Here's what I would to do intuitively: 这是我要直观地做的事情:
Foo<float> myfoo;
std::unique_ptr<Shape<float>> shape_ptr = myfoo.at(i);
shape_ptr->doSomething(param1, param2, ...);
When defining the at(index)
function, I'm getting a compiler error message. 在定义at(index)
函数时,我收到编译器错误消息。 Note that the move constructor was defined and that the Shape base class is abstract. 请注意,定义了移动构造函数,并且Shape基类是抽象的。 Below, I'm giving some code for illustration purposes. 下面,我将提供一些代码用于说明目的。
Furthermore, I found recently on the web an example on how to overload the assignment operator using std::move
. 此外,我最近在网上找到了一个关于如何使用std::move
重载赋值运算符的示例。 I usually follow the Copy-Swap idiom. 我通常遵循Copy-Swap习语。 Which of those two ways for overloading the mentioned operator makes sense for my case? 重载上述操作符的两种方法中的哪一种对我的情况有意义? Below, I'm also illustrating the function's definition. 下面,我还说明了函数的定义。
template < typename T >
class Foo{
public:
Foo();
Foo( Foo && );
~Foo();
void swap(Foo<T> &);
//Foo<T> & operator =( Foo<T> );
Foo<T> & operator =( Foo<T> && );
std::unique_ptr<Shape<T> > at ( int ) const; // error here!
int size() const;
private:
std::vector< std::unique_ptr<Shape<T> > > m_Bank;
};
template < typename T >
Foo<T>::Foo( Foo && other)
:m_Bank(std::move(other.m_Bank))
{
}
/*template < typename T >
void Filterbank<T>::swap(Filterbank<T> & refBank ){
using std::swap;
swap(m_Bank, refBank.m_Bank);
}
template < typename T >
Foo<T> & Filterbank<T>::operator =( Foo<T> bank ){
bank.swap(*this);
return (*this);
}*/
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank ){
//bank.swap(*this);
m_Bank = std::move(bank.m_Bank);
return (*this);
}
template < typename T >
std::unique_ptr<Shape<T> > Foo<T>::at( int index ) const{
return m_Bank[index]; // Error here! => error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
}
Use Boost's pointer containers instead of a standard container with unique_ptr. 使用Boost的指针容器而不是带unique_ptr的标准容器。 They're designed for this kind of usage. 它们是为这种用途而设计的。
I think you should be using shared_ptr here instead. 我认为你应该在这里使用shared_ptr 。
Only one unique_ptr can own the shared resource. 只有一个unique_ptr可以拥有共享资源。 If you are able to do what you intend ie return a unique_ptr by value then the one in the vector will be destroyed which is probably what you don't want. 如果你能够做你想做的事情,即按值返回unique_ptr那么向量中的那个将被销毁 ,这可能是你不想要的。
Q1: What to do with Foo::at( int ) const
such that you can: Q1:如何处理Foo::at( int ) const
,以便您可以:
myfoo.at(i)->doSomething(param1, param2, ...);
without transferring ownership out of the vector<unique_ptr<Shape<T>>>
. 不将所有权转移出vector<unique_ptr<Shape<T>>>
。
A1: Foo::at( int ) const
should return a const std::unique_ptr<Shape<T> >&
: A1: Foo::at( int ) const
应该返回一个const std::unique_ptr<Shape<T> >&
:
template < typename T >
const std::unique_ptr<Shape<T> >&
Foo<T>::at( int index ) const
{
return m_Bank[index];
}
Now your can dereference the const unique_ptr
and call any member of Shape
they want (const or non-const). 现在你可以取消引用const unique_ptr
并调用他们想要的任何Shape
成员(const或非const)。 If they accidentally try to copy the unique_ptr
, (which would transfer ownership out of Foo
) they will get a compile time error. 如果他们不小心尝试复制unique_ptr
(它会将所有权转移出Foo
),他们将收到编译时错误。
This solution is better than returning a non-const reference to unique_ptr
as it catches accidental ownership transfers out of Foo
. 这个解决方案比将非const引用返回到unique_ptr
要好,因为它会捕获Foo
意外所有权转移。 However if you want to allow ownership transfers out of Foo
via at
, then a non-const reference would be more appropriate. 但是,如果您希望允许从Foo
通过at
进行所有权转移,那么非const引用将更合适。
Q2: Furthermore, I found recently on the web an example on how to overload the assignment operator using std::move. Q2:此外,我最近在网上找到了一个关于如何使用std :: move重载赋值运算符的示例。 I usually follow the Copy-Swap idiom. 我通常遵循Copy-Swap习语。 Which of those two ways for overloading the mentioned operator makes sense for my case? 重载上述操作符的两种方法中的哪一种对我的情况有意义?
A2: I'm not sure what ~Foo()
does. A2:我不确定~Foo()
作用。 If it doesn't do anything, you could remove it, and then (assuming fully conforming C++11) you would automatically get correct and optimal move constructor and move assignment operator (and the proper deleted copy semantics). 如果它没有做任何事情,你可以删除它,然后(假设完全符合C ++ 11)你将自动获得正确和最佳的移动构造函数和移动赋值运算符(以及正确删除的复制语义)。
If you can't remove ~Foo()
(because it does something important), or if your compiler does not yet implement automatic move generation, you can supply them explicitly, as you have done in your question. 如果你不能删除~Foo()
(因为它做了一些重要的事情),或者你的编译器还没有实现自动移动生成,你可以明确地提供它们,就像你在问题中所做的那样。
Your move constructor is spot on: Move construct the member. 你的移动构造函数是现场:移动构造成员。
Your move assignment should be similar (and is what would be automatically generated if ~Foo()
is implicit): Move assign the member: 您的移动分配应该类似(并且如果~Foo()
是隐式的,则会自动生成):移动分配成员:
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank )
{
m_Bank = std::move(bank.m_Bank);
return (*this);
}
Your Foo
design lends itself to being Swappable
too, and that is always good to supply: 你的Foo
设计也适用于Swappable
,这总是很好供应:
friend void swap(Foo& x, Foo& y) {x.m_Bank.swap(y.m_Bank);}
Without this explicit swap
, your Foo
is still Swappable
using Foo
's move constructor and move assignment. 如果没有这种明确的swap
,你Foo
还是Swappable
使用Foo
的举动构造函数和移动赋值。 However this explicit swap
is roughly twice as fast as the implicit one. 然而,这种显式swap
速度大约是隐式swap
的两倍。
The above advice is all aimed at getting the very highest performance out of Foo
. 以上建议都旨在获得Foo
最高性能。 You can use the Copy-Swap idiom in your move assignment if you want. 如果需要,您可以在移动作业中使用复制交换习语。 It will be correct and slightly slower. 这将是正确的,稍慢。 Though if you do be careful that you don't get infinite recursion with swap
calling move assignment and move assignment calling swap
! 虽然如果你小心你没有通过swap
调用移动赋值和移动赋值调用swap
获得无限递归! :-) Indeed, that gotcha is just another reason to cleanly (and optimally) separate swap
and move assignment. :-)确实,这个问题只是干净(和最佳)分离swap
和移动分配的另一个原因。
Update 更新
Assuming Shape
looks like this , here is one way to code the move constructor, move assignment, copy constructor and copy assignment operators for Foo
, assuming Foo
has a single data member: 假设Shape
看起来像这样 ,这里有一种方法可以为Foo
编写移动构造函数,移动赋值,复制构造函数和复制赋值运算符,假设Foo
有一个数据成员:
std::vector< std::unique_ptr< Shape > > m_Bank;
... ...
Foo::Foo(Foo&& other)
: m_Bank(std::move(other.m_Bank))
{
}
Foo::Foo(const Foo& other)
{
for (const auto& p: other.m_Bank)
m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr));
}
Foo&
Foo::operator=(Foo&& other)
{
m_Bank = std::move(other.m_Bank);
return (*this);
}
Foo&
Foo::operator=(const Foo& other)
{
if (this != &other)
{
m_Bank.clear();
for (const auto& p: other.m_Bank)
m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr));
}
return (*this);
}
If your compiler supports defaulted move members, the same thing could be achieved with: 如果您的编译器支持默认的移动成员,则可以通过以下方式实现同样的目的:
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
for the move constructor and move assignment operator. 用于移动构造函数和移动赋值运算符。
The above ensures that at all times each Shape
is owned by only one smart pointer/vector/Foo. 以上确保每个Shape
始终只由一个智能指针/向量/ Foo拥有。 If you would rather that multiple Foo
s share ownership of Shape
s, then you can have as your data member: 如果您希望多个Foo
共享Shape
的所有权,那么您可以拥有作为您的数据成员:
std::vector< std::shared_ptr< Shape > > m_Bank;
And you can default all of move constructor, move assignment, copy constructor and copy assignment. 并且您可以默认所有移动构造函数,移动赋值,复制构造函数和复制赋值。
It seems like you should just be returning a reference here: 看起来你应该在这里返回一个引用:
Shape<T> & Foo<T>::at( int index ) const{
return *m_Bank[index];
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.