在stl容器中处理智能指针 [英] Handling smart pointers in stl container

查看:182
本文介绍了在stl容器中处理智能指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类 Foo< T> ,它有一个智能指针的向量到 Shape 派生类。
我试图在(index)成员函数实现。这里是我将直观地做:

  Foo< float& myfoo; 
std :: unique_ptr< Shape< float>> shape_ptr = myfoo.at(i);
shape_ptr-> doSomething(param1,param2,...);

在(index)函数中定义 ,我得到一个编译器错误消息。注意,移动构造函数是定义的,Shape基类是抽象的。



此外,我最近在网上找到一个如何使用 std :: move 。我通常遵循复制交换习语。重载上述操作符的两种方法中的哪一种对我的情况有意义?下面,我还说明了函数的定义。

  template<类型名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; //错误这里!

int size()const;

private:

std :: vector< std :: unique_ptr< Shape< T> > > m_Bank;
};

template<类型名T>
Foo< T> :: Foo(Foo&&&其他)
:m_Bank(std :: move(other.m_Bank))
{

}

/ * template<类型名T>
void Filterbank< T> :: swap(Filterbank< T& refBank){

using std :: swap;
swap(m_Bank,refBank.m_Bank);
}

template<类型名T>
Foo< T> & Filterbank< T> :: operator =(Foo T bank){

bank.swap(* this);
return(* this);
} * /

template<类型名T>
Foo & Foo T :: operator =(Foo T&&&&&&bank){

//bank.swap(*this);
m_Bank = std :: move(bank.m_Bank);
return(* this);
}

template<类型名T>
std :: unique_ptr< Shape< T> > Foo T :: ::(int index)const {
return m_Bank [index]; //错误这里! =>错误C2248:'std :: unique_ptr< _Ty> :: unique_ptr':无法访问在'std :: unique_ptr< _Ty>'中声明的私有成员
}
pre>

解决方案

Q1:如何处理 Foo :: at(int)const ,以便您可以:

  myfoo.at(i) - > doSomething(param1,param2,... );无需将所有权转移到向量< unique_ptr< Shape< T>>之外的



;>



A1: Foo :: at(int)const return a const std :: unique_ptr< Shape< T> &

 类型名T> 
const std :: unique_ptr< Shape< T> >&
Foo< T> :: at(int index)const
{
return m_Bank [index];
}

现在你可以引用const unique_ptr 并调用 Shape 的任何成员(const或非const)。如果他们不小心试图复制 unique_ptr (它将转移 Foo 的所有权),他们将获得一个编译时间错误。



此解决方案优于将非常量引用返回到 unique_ptr ,因为它捕获意外所有权转移的 Foo 。但是,如果你想允许通过通过 Foo 进行所有权转移,那么非const引用将更合适。



Q2:此外,我最近在网上找到一个关于如何使用std :: move重载赋值运算符的示例。我通常遵循复制交换习语。



A2:我不知道什么〜Foo()。如果它不做任何事情,你可以删除它,然后(假设完全符合C ++ 11),你会自动得到正确和最佳的移动构造函数和移动赋值运算符(和正确的删除的副本语义)。



如果你不能删除〜Foo()(因为它有重要的作用),或者如果你的编译器还没有实现



您的移动构造函数是现在:移动构造成员。



你的移动赋值应该是类似的(并且是〜Foo()是隐式的时会自动生成的):Move assign the member :

 模板<类型名T> 
Foo< T> & Foo T :: operator =(Foo T&&&&&&bank)
{
m_Bank = std :: move(bank.m_Bank);
return(* this);
}

您的 Foo 也适用于可插拔,这总是很好的供应:

  friend void swap(Foo& x,Foo& y){x.m_Bank.swap(y.m_Bank);} 


b $ b

没有这个显式的 swap ,你的 Foo 仍然 code>使用 Foo 的移动构造函数和移动赋值。然而,这个显式的 swap 的速度大约是隐式速度的两倍。



上述建议都是为了在 Foo 中性能最高。如果需要,您可以在移动分配中使用复制交换惯用法。这将是正确和稍慢。虽然如果你小心,你不会得到无限递归与 swap 调用移动分配和移动分配调用 swap ! :-)的确,这个秘密只是干净地(和最佳地)分离 swap 并移动赋值的另一个原因。



更新



假设 Shape 看起来像,这里是一种方法来代码移动构造函数,移动分配,复制构造函数和复制赋值运算符 Foo ,假设 Foo 有一个数据成员:

  std: :vector& std :: unique_ptr<形状> > 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> gt; 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);
}

如果你的编译器支持默认的移动成员,

  Foo(Foo&&)= 
Foo& operator =(Foo&&)= default;

用于移动构造函数和移动赋值运算符。



以上确保每一个 Shape 只有一个智能指针/向量/ Foo。如果你想多个 Foo 共享 Shape 的所有权,那么你可以作为你的数据成员: / p>

  std :: vector< std :: shared_ptr<形状> > m_Bank; 

你可以默认所有的移动构造函数,移动赋值,拷贝构造函数和拷贝赋值。 >

I've a class Foo<T> which has a vector of smart pointers to Shape derived classes. I'm trying to implement an at(index) member function. 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. Note that the move constructor was defined and that the Shape base class is abstract. 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. I usually follow the Copy-Swap idiom. 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>'
}

解决方案

Q1: What to do with Foo::at( int ) const such that you can:

myfoo.at(i)->doSomething(param1, param2, ...);

without transferring ownership out of the vector<unique_ptr<Shape<T>>>.

A1: Foo::at( int ) const should return a 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). If they accidentally try to copy the unique_ptr, (which would transfer ownership out of Foo) they will get a compile time error.

This solution is better than returning a non-const reference to unique_ptr as it catches accidental ownership transfers out of Foo. However if you want to allow ownership transfers out of Foo via at, then a non-const reference would be more appropriate.

Q2: Furthermore, I found recently on the web an example on how to overload the assignment operator using std::move. I usually follow the Copy-Swap idiom. Which of those two ways for overloading the mentioned operator makes sense for my case?

A2: I'm not sure what ~Foo() does. 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).

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.

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:

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:

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. However this explicit swap is roughly twice as fast as the implicit one.

The above advice is all aimed at getting the very highest performance out of 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! :-) Indeed, that gotcha is just another reason to cleanly (and optimally) separate swap and move assignment.

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:

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. If you would rather that multiple Foos share ownership of Shapes, then you can have as your data member:

std::vector< std::shared_ptr< Shape > > m_Bank;

And you can default all of move constructor, move assignment, copy constructor and copy assignment.

这篇关于在stl容器中处理智能指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆