【问题标题】:2d QList Memory Leak2d QList 内存泄漏
【发布时间】:2011-06-18 23:05:16
【问题描述】:

我创建了这个 2D Qlist。代码“有效”,但是当我更深入地考虑语法时,它让我想知道这段代码是否会导致内存泄漏。

如果有人能阐明这段代码是否会导致内存泄漏,我会非常高兴。如果是这样,为什么以及如何解决?

我的具体问题是代码 cmets 中标记为 Q1、Q2、Q3、Q4 的问题。

另外,我应该如何销毁 lszq2DList 中的一些行,最终我应该如何销毁 lszq2DList.

//The 2d data sructure. Each row will be a QStringList.
QList<QStringList> lszq2DList; 

//The variable "emptyTempList" is a concern to me.  
//Q1:: Isn't "emptyTempList" just a pointer to some allocated place in memory?  
//If so, I imagine it points to some data structure allocated in memory.
//Or does this not allocate anything?  I have a feeling it does not.
QStringList emptyTempList;   //I use it in my scheme to make a 2D data struct.

for(int i=0; i<3; i++){
    //Q2: Because I am in a loop and reusing the same pointer "emptyTempList", 
    //aren't i pointing to the same thing?
    //If I think about this, each item in my Qlist (each row) should be 
    //pointing to the same place in memory.  To whatever "emptyTempList"
    //points to.    
    //Of course, this is not the case.  What is really going on in line bellow?
    lszq2DList += emptyTempList;  //I'm no really pointing to anything am I?  

    QString szqTempString;  //Q3: Gets trashed when out of scope?
    for(int j=0; j<3; j++){
        szqTempString.clear();
        szqTempString = "jazz";

            //QString value gets copied into datastructure?
        lszq2DList[i] += szqTempString;  
    }
}


QStringList emptyTempList2;
emptyTempList2 += "blues";
emptyTempList2 += "blues";
emptyTempList2 += "blues";

//I'll add another row.
lszq2DList += emptyTempList2;  
//Q4: lszq2DList[3] does not point to emptyTempList2, right?
//Instead it copies all the strings 
//from to emptyTempList2 to lszq2DList[3], right?

【问题讨论】:

  • 您发布的代码甚至没有任何内存分配,它怎么可能有任何泄漏?

标签: c++ qt qt4


【解决方案1】:

QList、QStringList 和 QString 都是“隐式共享”的,这基本上意味着“写入时复制”。例如:

QStringList someFunction() {
    QStringList list;
    QString str("Hello World!");

    for(int x = 0;x < 100; ++x) {
        list << str;
    }

    QStringList list2 = list;
    QStringList list3 = list;
    QStringList list4 = list;

    return list4;
}

QStringList list5 = someFunction();

毕竟你仍然只有字符串“Hello World!”一次在记忆中。你可以阅读更多关于它的信息Here。作为 Qt 类的一般规则,“从 QObject 继承的对象放在堆上,否则它们放在堆栈上”。也有例外,但很少有隐式共享的类应该始终放在堆栈上。

【讨论】:

  • 谢谢,很棒的帖子。因为根据您的链接仅传递了指向数据的指针,所以 Qt 文档没有将其拼写出来,但我可以假设在您的示例中返回的“list4”是按值复制的 TRUE,因为否则当函数结束时我们会重新调整无效的引用,因为 func 中超出范围的元素被破坏。 Qt doc 说,当您“编写”时,它会进行深度复制。 Qt 文档不清楚何时深度复制。你会说在语法暗示按值操作复制并且该方案是线程安全的情况下假装 Qt 不执行“隐式共享”是安全的吗?
  • @user440297:“return list4”通过复制列表本身并增加对隐式共享内部的引用计数(写时复制)来创建 QStringList 的副本。 ref count == 2。然后随着范围的离开,'list4'被破坏,减少了ref。计算并留下您返回的副本。 ref count == 1。内部根本没有被复制,所以这是一个相对便宜的操作。
  • @user440297 基本上,只有在您在类上调用非常量方法时才会创建深层副本。
【解决方案2】:

我建议使用类似于 valgrind 或其他的东西来检查内存泄漏。

但是,当内存“泄漏”时,这意味着该内存在使用后没有被释放,并且所有访问都已被删除。例如,这实质上意味着在堆上分配了内存,但没有任何东西可以访问它,因此它实际上是无用的内存。例如,当您使用“new”为指针动态分配内存时,您必须“delete”以便释放内存。我举个例子吧。

char * ptr = new char;
ptr = NULL; // Memory leak
delete ptr; // This is useless since it no longer points to the memory location

但是,这是完全有效的

char * ptr = new char;
delete ptr;
ptr = NULL;

第一个泄漏的原因是因为您没有“删除”内存。由于在调用 delete 之前指针位置已更改为“NULL”,因此它不知道要在与指针关联的任何地址释放内存将其设置为 NULL。

不过,您的代码看起来不会有任何内存泄漏(没有动态分配)。由于看起来您的所有变量都在堆栈上分配,因此系统会为您管理此内存 - 一旦这些对象超出范围(如果这是在 main() 中,那么当程序退出时),它们将被弹出按系统堆叠。然而,这是假设 QList 没有泄漏。

编辑

请记住,当您创建类的实例时,分配的内存在堆栈上(内存在离开作用域时立即释放),您无需担心。动态分配(使用 new/delete 分配/释放内存)存储在堆上(用于我们目的的持久内存)。因此,您不必担心未动态分配的内存泄漏。

编辑 2

关于使用堆与堆栈,请尽可能使用堆栈。您必须管理的内存越少越好(并且您将更不容易出错)。就问题二而言,您没有使用指针。您正在做的是复制 emptyTempListvalue 而不是重用其内存位置(因为它不是指针)。您必须使用 &amp; 运算符从对象中提取内存地址,因为它不是指针。

希望这会有所帮助!

问候,
丹尼斯 M.

【讨论】:

  • 我 c,谢谢。我仍然对源代码中的 Q2:: 感到困惑。这就是我困惑的根源。话虽如此,您的回答让我问,如果我存储了大量信息(如文件系统扫描),是否应该在上面的示例中使用 new 。也就是说,我是否更有可能用完堆栈上的空间,所以我应该改用堆?
  • 我把我的回复加了一点,希望对你有帮助!
  • 是的,这有帮助,谢谢。然而,由于“emptyTempList”实际上并不包含数据,它只是复制了它的类型。这就说得通了。错误的?我来自 Java,这些东西都隐藏在那里,所以我从来不必考虑这种语法的基础。但就像 Katrina 指出的那样......实际上这似乎也不是传统 C-ASNI 意义上的价值复制.实际上,我们似乎只在处理复制指针地址……正如我在代码 cmets 中所关心的那样。所以问题是,即使在线程应用程序中,假装这是按值复制是否安全,即使实际上并非如此?
  • 在这种特殊情况下,您需要关注 += 运算符所做的任何事情。这是一个安全的操作,即使它不包含任何数据,因为运算符是在类中处理的,而不是由您处理的。考虑到这个想法,如果您是实现 += 运算符的人,那么您必须通过防止空指针来解决此类情况。请注意,内存泄漏在小范围内通常并不明显,但空指针会立即使程序崩溃。
猜你喜欢
  • 2013-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
相关资源
最近更新 更多