简体   繁体   English

处理stl容器中的智能指针

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM