处理 stl 容器中的智能指针

发布于 2024-12-21 02:23:05 字数 1904 浏览 5 评论 0 原文

我有一个 Foo 类,它有一个指向 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 重载赋值运算符的示例。我通常遵循复制交换习惯用法。重载上述运算符的两种方法中哪一种对我的情况有意义?下面,我还说明了该函数的定义。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

违心° 2024-12-28 02:23:05

使用 Boost 的指针容器而不是标准容器与 unique_ptr。它们是为这种用途而设计的。

Use Boost's pointer containers instead of a standard container with unique_ptr. They're designed for this kind of usage.

小傻瓜 2024-12-28 02:23:05

问题 1:如何使用 Foo::at( int ) const 以便您可以:

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

无需将所有权转移出 vector>> >。

A1: Foo::at( int ) const 应该返回一个 const std::unique_ptr; >&

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),他们将收到编译时错误。

此解决方案比返回对 unique_ptr 的非常量引用更好,因为它捕获了 Foo 中意外的所有权转移。但是,如果您希望允许通过 atFoo 转移所有权,那么非常量引用会更合适。

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

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 设计适合于 < code>Swappable 也是如此,而且提供起来总是好的:

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

如果没有这个显式的 swap,您的 Foo 仍然是 Swappable 使用 < code>Foo 的移动构造函数和移动赋值。然而,这种显式交换的速度大约是隐式交换的两倍。

上述建议都是为了获得 Foo 的最高性能。如果需要,您可以在移动分配中使用复制交换习惯用法。它会是正确的并且稍微慢一些。不过,如果您确实小心的话,请不要通过 swap 调用移动赋值和移动赋值调用 swap 来获得无限递归! :-) 事实上,这个陷阱只是干净(且最佳)分离交换和移动分配的另一个原因。

更新

假设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);
}

如果您的编译器支持默认的移动成员 方式实现:

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

,同样的事情可以通过以下 移动构造函数和移动赋值运算符。

上述内容确保在任何时候每个 Shape 仅由一个智能指针/向量/Foo 拥有。如果您希望多个 Foo 共享 Shape 的所有权,那么您可以将其作为数据成员:

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

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

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.

叶落知秋 2024-12-28 02:23:05

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

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

I think you should be using shared_ptr here instead.

Only one unique_ptr can own the shared resource. 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.

勿忘初心 2024-12-28 02:23:05

看来你应该在这里返回一个引用:

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

It seems like you should just be returning a reference here:

Shape<T> & Foo<T>::at( int index ) const{
    return *m_Bank[index];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文