【问题标题】:How to clean up class member of pointer to vector of pointers to a class?如何清理指向类指针向量的指针的类成员?
【发布时间】:2012-09-28 14:20:57
【问题描述】:

我有一个名为 Foo 的类,它有一个成员,它是指向另一个名为 Bar 的类的指针向量的指针。我在构造函数中初始化它,但我不确定如何在析构函数中释放它。我还在学习。感谢您的帮助。拥有此成员的原因是范围超出该方法即超出堆栈。谢谢。

#include <iostream>
#include <vector>

using namespace std;

class Bar {};

class Foo {
public:
    Foo() {
        bars = new vector<Bar*>*[10];

        for (int i = 0; i < 10; i++) {
            bars[i]->push_back(new Bar());
        }
    }

    ~Foo () {
        for (int i = 0; i < 10; i++) {
            // TODO: how to clean up bars properly?
        }
    }

private:
    vector<Bar*>** bars;
};

int main () {
    new Foo();
    return 0;
}

更新:感谢各方面的反馈。我是 C 和 C++ 的新手。基本上我想要一个 2d 结构作为类成员,它会在类的生命周期内持续存在。外部结构是一个数组的原因是因为我知道它需要多大。否则我之前使用的是向量向量。

【问题讨论】:

  • vector&lt;Bar*&gt; bars; 将在Foo 的生命周期内存在。 vector&lt;unique_ptr&lt;Bar&gt;&gt; bars;vector&lt;shared_ptr&lt;Bar&gt;&gt; bars; 让生活再次变得更轻松。
  • vector&lt;Bar*&gt;** bars;圣牛!
  • @AK4749 不,如果你看他是如何分配的,那不是错字。
  • 做三星级程序员可不是什么好事。另外:如果您真的想使用指针,请使用智能指针。你不能真的搞砸 std::shared_ptr
  • 您说您希望“范围在方法之外持续存在”。但是,bars 是私人成员,您不会像在工厂中那样退回它。此外,您还试图删除它。你能更详细地解释一下你想要做什么吗?

标签: c++


【解决方案1】:

这甚至没有正确分配。您分配了一个指向std::vector&lt;Bar*&gt; 的指针数组,但从来没有分配任何std::Vector&lt;Bar*&gt;

最好的办法就是像std::vector&lt;std::unique_ptr&lt;Bar&gt;&gt; 甚至std::unique_ptr&lt;std::vector&lt;std::unique_ptr&lt;Bar&gt;&gt;&gt; 或类似的东西。你得到的只是WTF。

std::unique_ptr&lt;std::array&lt;std::unique_ptr&lt;std::vector&lt;std::unique_ptr&lt;Bar&gt;&gt;&gt;, 10&gt;&gt;。这是完全匹配的(但自清洁)。

【讨论】:

    【解决方案2】:

    指针的数量有点荒谬,因为它们所做的只是造成混乱和泄漏,这从不正确的初始化和问题的标题中可以看出。您实际上根本不需要任何指针,也不必担心任何清理工作。

    对于第一个维度传入构造函数的二维数组,您可以使用向量的向量:

    std::vector<std::vector<Bar>> bars; 
    

    要使用传入的大小初始化外部向量,请使用初始化器:

    Foo(size_t size) 
        : bars(size) {}
    

    当对象被销毁时,bars 和它的所有元素也一样,所以不会忘记清理或清理不当。

    如果性能是一个问题,这可以转化为一种Matrix2D 类,它的作用类似于二维数组,但实际上只有一个底层的一维数组。

    【讨论】:

    • 谢谢克里斯。最后一件事。我实际上使用它有一个不同之处:我使用 Bar* 并通过执行 new Bar(...) 将对象添加到向量中。如何以一种不会像您的建议那样给我返回指针的方式使用构造函数 args 实例化 Bar?我对这一切还是很陌生。 (显然,如果我使用 Bar*,我将不得不相应地删除它们)。
    • @junkie,如果你想在后面推一个项目,你可以使用push_back。您的方式可能类似于bars[0].push_back(new Bar(5));,而非指针版本可能类似于bars[0].push_back(Bar(5));
    【解决方案3】:

    编辑:在 2D 结构的情况下(你说你知道你的 2D 结构需要多大,所以我们假设你的循环中的 10 是你想要的 2D 数组的大小。

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class Bar {
    public:
        int BarInfo;
    };
    
    class Foo {
    public:
        Foo() {
            // Allocates 10 vector spots for 10 bar elements - 100 bars, 10 x 10
            for (int i = 0; i < 10; i++) {
                // Puts 10 bars pointer at the end;
                // Heap-allocated (dynamically), which
                // means it survives until you delete it
                // The [10] is array syntax, meaning you'll have 10 bars
                // for each bars[index] and then
                // you can access it by bars[index][0-9]
                // If you need it to be more dynamic
                // then you should try vector<vector<Bar>>
                bars.push_back(new Bar[10]);
            }
        }
    
        Bar* operator[] (int index) {
            return bars[index];
        }
    
        ~Foo () {
            // Cleanup, because you have pointers inside the vector,
            // Must be done one at a time for each element
            for (int i = 0; i < bars.size(); i++) {
                // TODO: how to clean up bars properly?
                // Each spot in the vector<> contains 10 bars,
                // so you use the array deletion
                // and not just regular 'delete'
                delete[] bars[i]; // deletes the 10 array elements
            }
        }
    
    private:
        // vector might not be a bad idea.
        vector<Bar*> bars;
    };
    

    这是我测试代码的主要部分,它的工作方式就像你认为二维数组应该工作一样:

    int main ( int argc, char* argv[] ) {
        Foo foo;
        // Bar at 1st row, 2nd column ( row index 0, column index 1 )
        // Bar& is a reference
        Bar& bar12 = foo[0][1];
        bar12.BarInfo = 25;
        int stuffInsideBar = foo[0][1].BarInfo; // 25
        return 0;
    }
    

    我希望这对您有所帮助,并使您更接近您正在做的事情。我在这里使用了一种技术,这种技术可能会让初学者头疼,以使Foo 类的行为就像您认为的二维数组一样。它被称为operator overloading。它是 C++ 中的一项强大功能,因此一旦您掌握了更多基础知识,它可能对您未来的项目或当前项目有用。祝你好运!

    ~~~~~~~~~~~~~~~~~~~~~~~~~~

    编辑前的旧答案

    ~~~~~~~~~~~~~~~~~~~~~~~~~~

    看来你做的间接太多了。虽然另一个人的回答向您展示了如何清理您已经设法做的事情,但我认为您可以从改变您处理课程的方式中受益。

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class Bar {};
    
    class Foo {
    public:
        Foo() : bars() {
            // bars is no longer a pointer-to-vectors, so you can just
            // allocate it in the constructor - see bars() after Foo()
            //bars = new vector<Bar>();
    
            for (int i = 0; i < 10; i++) {
                // Puts 1 bar pointer at the end;
                // Heap-allocated (dynamically), which
                // means it survives until you delete it
                bars.push_back(new Bar());
            }
        }
    
        ~Foo () {
            // Cleanup, because you have pointers inside the vector,
            // Must be done one at a time for each element
            for (int i = 0; i < 10; i++) {
                // TODO: how to clean up bars properly?
                // TODOING: One at a time
                delete bars[i]; // deletes pointer at i
            }
        }
    
    private:
        // You don't need to ** the vector<>,
        // because it's inside the class and 
        // will survive for as long as the class does
        // This vector will also be copied to copies of Foo,
        // but the pointers will remain the same at the time
        // of copying.
        // Keep in mind, if you want to share the vector, than
        // making it a std::shared_ptr of a
        // vector might not be a bad idea.
        vector<Bar*> bars;
    };
    

    如果您通过引用将类传递给函数,则类中的 vector&lt;Bar*&gt; 不会复制自身或删除自身,从而使其在单个堆栈帧之后持续存在。

    在您的main 中,这应该可以正确清理,并且比 vector** 更容易跟踪。但是,如果由于某种原因需要矢量**,那么 home_monkey 的答案应该可以帮助您更多。

    【讨论】:

    • 谢谢。但是,对不起,我应该在我原来的帖子中说,我正在寻找一个 2d 结构而不是 1d。我已经详细说明了对我的原始帖子和帖子 cmets 的更新。
    • 我已经编辑了答案,以包含一个我认为 OP 想要的明确示例。如果不正确,请告诉我。
    • 再次感谢。我知道我的外部维度的大小,但不知道内部维度。
    • @junkie 那么最好将它放在 std::array&lt;vector&lt;Bar&gt;, OUTERDIMENSIONSIZE&gt; bars 的配置中。 std::array 类代表一个固定数组,这意味着您不会浪费空间。内部vector&lt;Bar&gt; 应该为您提供所需的动态内部尺寸。它应该可以像bars[0][1] 一样访问,您可以通过bars[targetrow].push_back( mybarelement ) 添加任意数量的Bar
    • 或向量的向量,如在 v.size() 中会给他内部索引,而 v[i].size() 会给他外部索引。 +1 的努力。取消 -1。
    【解决方案4】:

    我认为分配存在问题。线

    new vector <Bar*> * [10] 
    

    会给你一个指向vector &lt;Bar*&gt; *类型的对象的数组,你 将需要为您的 vector &lt;Bar*&gt; 类型分配一些额外的内存。

    我已经尝试过了,

    Foo() 
    {
        bars = new vector<Bar*>*[10]; // A.
    
        for (int i = 0; i < 10; i++) 
        {
            bars[i] = new vector<Bar*>;  //  B. Extra memory assigned here.
            bars[i]->push_back(new Bar); //  C.
        }
    }
    

    要释放资源,您必须颠倒上述操作

    ~Foo () 
    {
        for (int i = 0; i < 10; i++) 
        {
            //  I'm assuming that each vector has one element
            delete bars[i][0]; //  Free C 
            delete bars[i];    //  Free B    
        }
    
        delete [] bars; // Free A
    }
    

    【讨论】:

    • 干得好,你给出的解释比我的回答更笼统。
    【解决方案5】:

    我不相信你甚至没有正确分配它。要撤消您迄今为止所做的一切,您只需:

    for(int i=0; i < 10; ++i)
        while(bars[i]->size > 0)
        {
            delete *bars[i]->front();
            pop_front();
        }
    delete[] bars;
    

    但是,您需要自己分配每个向量,例如在构造函数中:

    for(int i=0; i<10; ++i)
        bars[i] = new vector<Bar*>();
    

    这需要您将析构函数更改为:

    for(int i=0; i < 10; ++i)
    {
        while(bars[i]->size > 0)
        {
            delete *bars[i]->front();
            pop_front();
        }
        delete bars[i];
    }
    delete[] bars;
    

    关于您的更新,我建议将栏的类型更改为:

    vector<Bar*>* bars;
    

    以及分配给(无需执行上面建议的 for 循环分配):

    bars = new vector<Bar*>[10];
    

    【讨论】:

    • 到目前为止,每个元素都被单独newed。
    • 这只会去掉向量数组,而不是它们指向的东西。
    • @chris 是的,我没发现他在分配酒吧。
    • 是的,好吧,他原来的帖子并不清楚他为什么想要矢量指针,所以我就跟着玩了。如您所见,我稍后建议他只是更改类型。
    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 2014-11-14
    • 2017-07-27
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多