处理stl容器中的智能指针

时间:2011-12-12 19:46:06

标签: c++ stl c++11 smart-pointers unique-ptr

我有一个类Foo<T>,它有一个智能指针向Shape派生类的向量。 我正在尝试实现at(index)成员函数。这就是我要直观地做的事情:

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

定义at(index)函数时,我收到编译器错误消息。请注意,定义了移动构造函数,并且Shape基类是抽象的。下面,我将提供一些代码用于说明目的。

此外,我最近在网上找到了一个关于如何使用std::move重载赋值运算符的示例。我通常遵循Copy-Swap习语。重载上述操作符的两种方法中的哪一种对我的情况有意义?下面,我还将说明函数的定义。

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>'
}

4 个答案:

答案 0 :(得分:2)

使用Boost's pointer containers代替带有unique_ptr的标准容器。它们是为这种用途而设计的。

答案 1 :(得分:1)

我认为你应该在这里使用 shared_ptr

只有一个 unique_ptr 可以拥有共享资源。如果你能够做你想做的事情,即按值返回unique_ptr ,那么向量中的那个将被销毁,这可能是你不想要的。

答案 2 :(得分:1)

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

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

未将所有权转移出vector<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];
}

现在你可以取消引用const unique_ptr并调用他们想要的Shape成员(const或非const)。如果他们意外地尝试复制unique_ptr(这会将所有权转移出Foo),则会出现编译时错误。

此解决方案优于将非const引用返回unique_ptr,因为它会从Foo中捕获意外的所有权转移。但是,如果您希望通过Foo允许从at转出所有权,那么非const引用会更合适。

Q2:此外,我最近在网上找到了一个关于如何使用std :: move重载赋值运算符的示例。我通常遵循Copy-Swap习语。重载上述运算符的两种方法中的哪一种对我的情况有意义?

A2:我不确定~Foo()是做什么的。如果它没有做任何事情,你可以删除它,然后(假设完全符合C ++ 11)你将自动获得正确和最佳的移动构造函数和移动赋值运算符(以及正确删除的复制语义)。

如果您无法删除~Foo()(因为它确实有重要意义),或者您的编译器尚未实现自动移动生成,您可以明确地提供它们,就像您在问题中所做的那样。

你的移动构造函数是现场:移动构造成员。

您的移动分配应该类似(并且如果隐含~Foo()则会自动生成):移动分配成员:

template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank )
{
    m_Bank = std::move(bank.m_Bank);
    return (*this);
}

您的Foo设计也适用于Swappable,这总是很有用:

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

如果没有这个明确的swap,您的Foo仍然Swappable使用Foo的移动构造函数并移动分配。然而,这个显式的swap大约是隐式的Foo的两倍。

上述建议旨在获得swap以外的最高性能。如果需要,您可以在移动作业中使用复制交换习语。这将是正确的,稍慢。虽然如果你确实小心,你没有通过swap调用移动赋值和移动赋值调用swap来获得无限递归! :-)确实,这个问题只是干净(并且最佳地)分离Shape并移动任务的另一个原因。

<强>更新

假设Foo看起来像this,这里有一种方法可以为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); } 单个数据成员:

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

...

Shape

如果您的编译器支持默认移动成员,则可以通过以下方式实现相同的目的:

Foo

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

以上确保每个Shape始终只由一个智能指针/向量/ Foo拥有。如果您希望多个std::vector< std::shared_ptr< Shape > > m_Bank; 共享{{1}} s的所有权,那么您可以拥有数据成员:

{{1}}

您可以默认所有移动构造函数,移动赋值,复制构造函数和复制赋值。

答案 3 :(得分:0)

好像你应该在这里返回一个引用:

Shape<T> & Foo<T>::at( int index ) const{
    return *m_Bank[index];
}