【问题标题】:Adding Elements to std::vector of an abstract class将元素添加到抽象类的 std::vector
【发布时间】:2015-10-03 08:09:44
【问题描述】:

我想将派生自公共接口(抽象类)的类的对象存储在该抽象类的 std::vector 中。这个向量应该在一个循环中填充,通常我会调用一个类的构造函数并将创建的对象推送到向量中。

据我了解,对于抽象类,我只能存储指向该类的指针,因此我需要 push_back 派生类的指针。但是,我不确定这些新创建的对象的范围。

请看下面的代码。这段代码可以编译并且运行良好,但我的问题是:

a) 对象是否保证存在于 main 函数的第二个 for 循环中?或者它们是否会在创建它们的循环范围之外停止存在?

b) 是否调用了所有对象的析构函数或者是否存在内存泄漏?

#include<vector>
#include<iostream>
class Interface {
    public:
    Interface( int y ) : x(y) {}
    virtual ~Interface() {}
    virtual void f() = 0;
    int x;  
};

class Derived_A : public Interface {
    public:
    Derived_A( int y ) : Interface(y) {}
    void f(){ return; }
};

class Derived_B : public Interface {
    public:
    Derived_B( int y ) : Interface(y) {}
    void f(){ return; }
};


int main()
{
    std::vector<Interface*> abstractObjects;
    int N = 5;
    for(int ii = 0; ii < N; ii++ )
    {
        abstractObjects.push_back( new Derived_A(ii) );
        abstractObjects.push_back( new Derived_B(ii) );
    }

    for(int ii = 0; ii < abstractObjects.size(); ii++ )
    {
        abstractObjects[ii]->f();
        std::cout << abstractObjects[ii]->x << '\t' << std::endl;
    }


    for(int ii = 0; ii < abstractObjects.size(); ii++ )
    {
        delete abstractObjects[ii];
    }

    return 0;
}

【问题讨论】:

    标签: c++ vector


    【解决方案1】:

    这是智能指针的完美案例。您可以将指针存储在 RAII 类型的 unique_ptr 中。当unique_ptr 超出范围时,它会自动为您删除内存。

        //...
        std::vector<std::unique_ptr<Interface>> abstractObjects;
        int N = 5;
        for(int ii = 0; ii < N; ii++ )
        {
            abstractObjects.push_back( std::make_unique<Derived_A>(ii) );
            abstractObjects.push_back( std::make_unique<Derived_B>(ii) );
        }
    
        for(auto & e : abstractObjects)  // ranged based for loop
        {
            e->f();
            std::cout << e->x << '\t' << std::endl;
        }
        // no need to do anything here.  the vector will get rid of each unique_ptr and each unique_ptr will delete each pointer
        return 0;
    }
    

    【讨论】:

    • 如果仍然使用 c++14 为什么不使用范围循环呢?
    • @Slava 感谢您的建议。我编辑了代码。
    • 谢谢!如果我有一个函数f2() 按照您的建议创建向量,然后使用这个精确向量创建一个对象Obj(std::vector&lt;std::unique_ptr&lt;Interface&gt; &gt;) 并从函数返回这个Obj,会发生什么?调用f2()的函数中是否仍存在向量(及其元素)?
    • @seyfe `unique_ptr 不可复制。因此,您只能引用包含它们的向量,或者您可以 move 将向量指向您想要的位置
    • 很好的参考资料,我喜欢阅读!如果我猜对了,这意味着将构造函数Obj( std::vector&lt;std::unique_ptr&lt;Interface&gt; &gt;) 调用为std::vector&lt;std::unique_ptr&lt;Interface&gt; &gt; v; Obj o( std::move( v ) ); 将向量v 的所有权转移到对象o,因此向量将在o 被销毁的同时被销毁?
    【解决方案2】:

    让我谈谈你的观点。

    a) 对象是否保证存在于第二个 for 循环中 主功能?或者它们可能会停止存在超出范围 它们是在哪个循环中创建的?

    当您调用关键字new 时,对象将一直存在,直到您明确对它们调用delete 以释放相关的内存。如果您在堆栈上创建了对象,那么它们将在第一个循环终止后超出范围。

    b) 是否调用了所有对象的析构函数,或者是否存在内存泄漏?

    是的,您在最终循环中正确调用了每个对象的析构函数,并且通常不会出现内存泄漏。但是,如果在到达最终循环之前引发异常,则分配的内存将不会被回收,并且您发生泄漏。见this post

    但是,您可以通过使用智能指针来改进您的代码,智能指针通过自动回收内存解决了这个问题。使用std::make_unique&lt;Derived_A&gt;(ii) 而不是new Derived_A(ii),当向量超出范围时,它会自动释放它包含的每个对象的关联内存,从而无需在最终循环中自己显式调用析构函数。

    【讨论】:

    • "并且不应该有内存泄漏。"也许你应该提到异常也可能导致内存泄漏,这是使用智能指针的另一个原因。
    【解决方案3】:

    是的,这些对象仍然存在于您的第一个 for 循环范围之外,因此您对 delete 它们是正确的。

    并非所有对象的析构函数都会被自动调用。如果你 new 某事,那么你必须 delete 它。但是,您可以(并且应该)使用智能指针。

    std::vector<std::unique_ptr<Interface>> abstractObjects;
    int N = 5;
    for(int ii = 0; ii < N; ii++ )
    {
        abstractObjects.push_back( std::make_unique<Derived_A>(ii) );
        abstractObjects.push_back( std::make_unique<Derived_B>(ii) );
    }
    

    现在你不必delete 任何东西,当vector 超出范围时将调用析构函数(因此它的所有元素也是如此)

    【讨论】:

      【解决方案4】:

      我建议使用 智能指针vector(而不是原始的拥有指针)。虽然原始 observing 指针很好,但拥有原始 owning 指针向量可能是泄漏的潜在来源。

      对于非共享所有权,请考虑使用 vector&lt;unique_ptr&lt;Interface&gt;&gt;,并使用 std::make_unique 动态创建要添加到向量中的新项目。

      使用 smart 指针向量可以让您编写更简单、更清晰的代码,因为清理是自动完成的,这要归功于 C++ 析构函数(换句话说,所有手动清理原始拥有指针向量所需的代码就消失了)。

      【讨论】:

        猜你喜欢
        • 2022-01-19
        • 2016-04-06
        • 2015-05-16
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 2016-03-30
        • 2011-12-27
        • 1970-01-01
        相关资源
        最近更新 更多