为什么重新分配unique_ptr会增加内存使用量?

时间:2014-09-21 03:38:07

标签: c++ polymorphism unique-ptr

我有一个基类,以及它的一些变形。我希望能够创建类型为base的对象,然后将其变换为派生类,并返回到base。可以这样做吗?我这样做,但我不确定这是正确的方法:

假设我有这些课程:

class base {
public:
    int var;
    base();
    virtual ~base();
    virtual void func1();
    void func2();
}

class derived1 : base {
public:
    unique_ptr<otherClass> otherVar;
    vector<unique_ptr<anotherClass>> myVec;
    derived1();
    ~ derived1() {
    otherVar.reset();
    for (unsigned int i=0; i< myVec.size(); i++) {
    myVec[i].reset();
    }
    myVec.clear();
    vector<unique_ptr< anotherClass >>().swap(myVec);
    }
    void  func1(){
       //do something
    }
}

class derived2 : base {
public:
    derived2();
    ~ derived2();
    void  func1(){
       //do something else
    }
}

现在代码中的其他地方我这样做:

  unique_ptr<base> myObject;
  myObject = unique_ptr< derived1 > (new derived1());

如果以后我这样做:

  myObject.reset();
  myObject = unique_ptr< base > (new base());

堆内存显着增加。是什么原因,以及如何避免这个问题?

EDIT1

我为derived1添加了更多细节。 我没有错误,程序运行没有问题,只有内存在myObject变形时上升。 顺便说一句,myObject永远不会超出范围,它只会改变它的形状(所以我不确定我使用的是正确的指针)。

EDIT2

我设法摆脱了类中的大多数指针(遗憾的是我无法在所有情况下都使用普通变量),现在我的内存增长非常少。 但我的理解是,使用智能指针应该让生活更轻松而不是更难。我们可以使用C ++中的任何类型的指针,释放内存而不删除它(我认为reset()应该这样做)然后再重新分配一些其他值吗?

我必须说,对于简单的对象,这可以通过unique_ptr来完成,但对于复杂的类(其他类指针作为其成员变量),似乎即使在调用reset之后,某些内存也永远不会被释放( )。

EDIT3

我发现问题不在于代码的C ++部分,而在于OpenGL部分。很抱歉这里的专家很困惑XD。

我这样做的时候:

    otherVar.reset();

此otherVar包含OpenGL纹理。这个对象的析构函数应该一次删除所有纹理(至少在Mac上):

glDeleteTextures(textureCount, textures);

但是在iOS(OpenGL ES)上,我不得不单独删除纹理,如下所示:

glDeleteTextures(1, &tex.textureID);

尽管如此,我还不知道为什么glDeleteTextures(textureCount,textures)在iOS应用中不会立即删除所有纹理,但至少我找到了一种解决方法。

谢谢大家。

1 个答案:

答案 0 :(得分:0)

无法确定,但我怀疑代码中可能存在循环所有权模式。以下是一个简单示例中的内容:

#include <iostream>
#include <memory>

struct B;
struct C;

struct A
{
    std::unique_ptr<B> b_ptr_;

    ~A();
};

struct B
{
    std::unique_ptr<C> c_ptr_;

    ~B();
};

struct C
{
    std::unique_ptr<A> a_ptr_;

    ~C();
};


A::~A()
{
    std::cout << "~A()\n";
}

B::~B()
{
    std::cout << "~B()\n";
}

C::~C()
{
    std::cout << "~C()\n";
}

int
main()
{
    std::unique_ptr<A> a_ptr(new A);
    a_ptr->b_ptr_ = std::unique_ptr<B>(new B);
    a_ptr->b_ptr_->c_ptr_ = std::unique_ptr<C>(new C);
    a_ptr->b_ptr_->c_ptr_->a_ptr_ = std::move(a_ptr);
}

编译运行时,该程序没有输出。这是令人担忧的,因为ABC的析构函数未运行,表明内存泄漏。

有许多方法可以解决循环所有权问题。但第一步是了解你的内存所有权图,并确保它是非循环的,并且使用unique_ptr,还必须确保图中的任何节点只有一个(拥有)父节点。

以下是打破此示例所有权周期的一种方法:

#include <iostream>
#include <memory>

struct B;
struct C;

struct A
{
    std::unique_ptr<B> b_ptr_;

    ~A();
};

struct B
{
    std::unique_ptr<C> c_ptr_;

    ~B();
};

struct C
{
    A* a_ptr_;

    ~C();
};


A::~A()
{
    std::cout << "~A()\n";
}

B::~B()
{
    std::cout << "~B()\n";
}

C::~C()
{
    std::cout << "~C()\n";
}

int
main()
{
    std::unique_ptr<A> a_ptr(new A);
    a_ptr->b_ptr_ = std::unique_ptr<B>(new B);
    a_ptr->b_ptr_->c_ptr_ = std::unique_ptr<C>(new C);
    a_ptr->b_ptr_->c_ptr_->a_ptr_ = a_ptr.get();
}

正确输出:

~A()
~B()
~C()

即。插入一个非拥有的&#34;原始指针&#34;进入循环。也可以使用shared_ptrweak_ptr打破周期:

#include <iostream>
#include <memory>

struct B;
struct C;

struct A
{
    std::shared_ptr<B> b_ptr_;

    ~A();
};

struct B
{
    std::shared_ptr<C> c_ptr_;

    ~B();
};

struct C
{
    std::weak_ptr<A> a_ptr_;

    ~C();
};


A::~A()
{
    std::cout << "~A()\n";
}

B::~B()
{
    std::cout << "~B()\n";
}

C::~C()
{
    std::cout << "~C()\n";
}

int
main()
{
    std::shared_ptr<A> a_ptr(new A);
    a_ptr->b_ptr_ = std::shared_ptr<B>(new B);
    a_ptr->b_ptr_->c_ptr_ = std::shared_ptr<C>(new C);
    a_ptr->b_ptr_->c_ptr_->a_ptr_ = std::move(a_ptr);
}

~A()
~B()
~C()

如果我认为循环所有权是您案件的罪魁祸首,那么通过审核,理解和记录代码中的所有权路径,您的时间就不会完全被浪费。