简体   繁体   English

将指针向量作为类成员进行管理

[英]Management of a vector of pointers as a class member

I am in a rather specific situation that requires me to use a vector of raw pointers as a class member:我处于一种相当特殊的情况,需要我使用原始指针向量作为类成员:

Here is a basic example of what I need:这是我需要的基本示例:

#include <vector>

class Foo
{
    virtual void foo() {}
};

class Bar : public Foo
{
    void foo(){}
};

class FooBar
{
    vector<Foo*> list;
    void add(){ list.push_back(new Bar()); }
};

Coming from Java, I am very scared of pointers and memory leaks.来自 Java,我非常害怕指针和内存泄漏。 This post initially suggests to delete the objects manually when the object goes out of scope. 这篇文章最初建议在对象超出范围时手动删除对象。 Is it enough to do this in the destructor of FooBar ?FooBar的析构函数中这样做就足够了吗?

class FooBar
{
    vector<Foo*> list;
    void add(){ list.push_back(new Bar()); }

    ~FooBar(){
         for(vector<Foo*>::iterator it=list.begin(); it!=list.end(); it++)
             delete *it;
    }

};

By the rule of three , I guess I also have to implement a copy constructor and an assignment operator.根据三规则,我想我还必须实现一个复制构造函数和一个赋值运算符。 Is the following (based on this and that posts) a correct implementation ?以下(基于这个那个帖子)是正确的实现吗?

FooBar::FooBar(const FooBar& orig) : list(orig.list.size()) {
    try {
        vector<Foo*>::iterator thisit = list.begin();
        vector<Foo*>::const_iterator thatit = orig.list.cbegin();

        for (; thatit != orig.list.cend(); ++thisit, ++thatit)
            *thisit = *thatit;  // I'm okay with a shallow copy

    } catch (...) {
        for (vector<Foo*>::iterator i = list.begin(); i != list.end(); ++i)
            if (!*i)
                break;
            else
                delete *i;

        throw;
    }
}

FooBar& operator=(const FooBar& orig){
    FooBar tmp(orig);
    swap(tmp);
    return *this;
}

Your code has some major flaws as far as I can tell.据我所知,您的代码存在一些重大缺陷。 First of all, your first code is correct, and yes, your destructor does what it should do, as long as your class owns the objects and doesn't get copied.首先,你的第一个代码是正确的,是的,你的析构函数会做它应该做的事情,只要你的类拥有对象并且不被复制。

Also, your copy constructor and assignment operator looks problematic.此外,您的复制构造函数和赋值运算符看起来有问题。 First of all, if you are okay with shallow copying, you don't even have to write the functions anyway, std::vector 's copy constructor and assignment operators do what you have done manually anyway.首先,如果你对浅拷贝没问题,你甚至不必编写函数, std::vector的复制构造函数和赋值运算符无论如何都会做你手动完成的事情。 I don't think you need that try-catch block.我认为您不需要那个 try-catch 块。 By the way, where is the implementation for that swap in your assignment operator?顺便说swap ,您的赋值运算符中该swap的实现在哪里?

For your purposes, a very small reference counting scheme would work:出于您的目的,一个非常小的引用计数方案将起作用:

class FooPtr
{
     Foo* _raw;
     size_t* _ctl;

     FooPtr(Foo* ptr) : 
         _raw(ptr), _ctl(new size_t(0))
     {
     }

     template <class T>
     FooPtr(T* ptr) : FooPtr(static_cast<Foo*>(ptr)) {}

     FooPtr(const FooPtr& rhs) : 
         _raw(rhs._raw), _ctl(rhs._ctl)
     {
         ++(*_ctl);
     }

     ~FooPtr()
     {
        if (_raw == nullptr) return;
        --(*_ctl);
        if (*_ctl == 0)
        {
            delete _raw;
            delete _ctl;
        }
     }

     FooPtr& operator=(FooPtr ptr)
     {
         std::swap(*this, ptr);
         return *this;
     }

     Foo* operator->()
     {
         return _raw;
     }

     Foo& operator*()
     {
         return *_raw;
     }
}

Your class now looks like this, no destructor, copy ctor or assignment operator needed:你的类现在看起来像这样,不需要析构函数、复制构造函数或赋值运算符:

class FooBar
{
     vector<FooPtr> list;
     void add(){ list.emplace_back(new Bar()); }
}

I've written this quickly, it might contain bugs though it gives you the basic idea I suppose.我写得很快,它可能包含错误,尽管它为您提供了我认为的基本思想。 Also, I've used some c++11 features like emplace_back and nullptr but they can easily be converted in non 11 code.此外,我使用了一些 c++11 功能,如emplace_backnullptr但它们可以很容易地转换为非 11 代码。

I would write a small wrapper around a vector that frees the elements for you:我会围绕一个vector编写一个小包装器,为您释放元素:

template<class Container>
void delete_elements(Container& cont) {
    typedef typename Container::reverse_iterator iterator;
    iterator end = container.rend();
    for (iterator i = container.rbegin(); end != i; ++i) {
        delete *i;
    }
}

struct deep_copy {
    template<typename T>
    T* operator()(T const* const other) {
        return other->clone();
    }
}

template<typename T>
struct ptr_vector {
    std::vector<T*> container;

    ptr_vector() { }

    ptr_vector(ptr_vector const& other) {
        std::vector<T*> tmp;
        tmp.reserve(other.container.size());

        try {
            std::transform(other.container.begin(), other.container.end(),
                    std::back_inserter(tmp), deep_copy());
        }
        catch (...) {
            (delete_elements)(tmp);
            throw;
        }

        container.swap(tmp);
    }

    ptr_vector& operator=(ptr_vector other) {
        container.swap(other.container);
        return *this;
    }

    ~ptr_vector() {
        (delete_elements)(container);
    }
};

Now you can just use a ptr_vector<Foo> v and use v.container to add elements.现在你可以只使用ptr_vector<Foo> v并使用v.container添加元素。 Note: you should add a clone member function to your base class so you don't slice when you want to copy.注意:你应该在你的基类中添加一个clone成员函数,这样当你想要复制时就不会切片。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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