【问题标题】:Management of a vector of pointers as a class member将指针向量作为类成员进行管理
【发布时间】:2016-03-11 10:01:45
【问题描述】:

我处于一种相当特殊的情况,需要我使用原始指针向量作为类成员:

这是我需要的基本示例:

#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()); }
};

来自 Java,我非常害怕指针和内存泄漏。 This post 最初建议在对象超出范围时手动删除对象。在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;
    }

};

通过rule of three,我想我还必须实现一个复制构造函数和一个赋值运算符。以下(基于thisthat 帖子)是正确的实现吗?

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

【问题讨论】:

  • // I'm okay with a shallow copy 这与析构函数的实现一起将打破你的脖子。您将尝试释放多次分配的空间。
  • 无法理解“没有智能指针库”。您可以随时从智能指针获取原始指针。
  • 为什么是 try/catch?抛出什么?
  • @πάνταῥεῖ,那么是析构函数错误还是复制构造函数错误?
  • 如果 no smart pointer library (because of constraints from the hardware I'm using). 然后编写您自己的简单共享智能指针并计算链接。没有想象中那么难。您当前的代码(复制方法)将泄漏内存

标签: c++ pointers vector


【解决方案1】:

据我所知,您的代码存在一些重大缺陷。首先,你的第一个代码是正确的,是的,只要你的类拥有对象并且没有被复制,你的析构函数就会做它应该做的事情。

另外,您的复制构造函数和赋值运算符看起来有问题。首先,如果你对浅拷贝没问题,你甚至不必编写函数,std::vector 的复制构造函数和赋值运算符无论如何都会做你手动完成的事情。我认为您不需要那个 try-catch 块。顺便问一下,您的赋值运算符中的 swap 的实现在哪里?

为了您的目的,一个非常小的引用计数方案可以工作:

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

您的类现在看起来像这样,不需要析构函数、复制 ctor 或赋值运算符:

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

我写得很快,虽然它给了你我想的基本概念,但它可能包含错误。此外,我使用了一些 c++11 功能,例如 emplace_backnullptr,但它们可以很容易地转换为非 11 代码。

【讨论】:

  • 谢谢!我正在寻求这个解决方案(尽管我从 @nikniknik2016 的链接开始实施)。
【解决方案2】:

我会围绕 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);
    }
};

现在您可以只使用ptr_vector&lt;Foo&gt; v 并使用v.container 添加元素。注意:你应该在你的基类中添加一个clone 成员函数,这样你就不会在你想要复制的时候切片。

【讨论】:

  • 谢谢。我还没有测试过,但它看起来是个不错的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-28
  • 2015-02-15
  • 2018-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多