【问题标题】:How can I return a QList<specificObject*> pointer and get no memory leak?如何返回 QList<specificObject*> 指针并且没有内存泄漏?
【发布时间】:2015-12-15 00:50:52
【问题描述】:

我的数据库中有一个问题和答案表。这两者之间的关系是一个问题可以有多个答案。在我的 C++ 代码中,我有一个 Question 普通对象,它存储问题文本和答案的 QList。 Answer 普通对象只存储答案文本。

我想阅读与这些问题相关的所有问题和所有答案,并将它们存储在我的问题对象中。这就是我所做的。

QList<Question*> *ExamPersistance::readQuestions(int examId)
{
    QList<Question*> *questions;
    QSqlQuery *query = new QSqlQuery(connection->getDb());
    query->prepare("select q.id, q.text from question q where id_exam =:examId");
    query->bindValue(":examId",examId);
    query->exec();
    while(query->next())
    {
        questions->append(new Question(
                             query->value(1).toString(),
                                       this->readAnswers(query->value(0).toInt())));
    }
    return questions;
}

QList<Answer*> *ExamPersistance::readAnswers(int questionId)
{
    QList<Answer*> *answers;
    QSqlQuery *query = new QSqlQuery(connection->getDb());
    query->prepare("select a.text from answer a where id_question = :questionId");
    query->bindValue(":questionId",questionId);
    query->exec();
    while(query->next())
    {
        answers->append(new Answer(query->value(0).toString()));
    }
    return answers;
}

我遇到分段错误,我不知道为什么。

这些是我的头文件中的构造函数

 Answer(QString text);
 Question(QString name, QList<Answer*> *answers);

也许我的指针做错了。我不得不承认我对这些不是很熟悉,因为我来自 Java。

编辑:

我还有一个类“考试”,其中存储了某个考试的问题,但我认为这并不重要。只是你知道这个“int ExamId”代表什么

【问题讨论】:

  • Segfault != 内存泄漏
  • 您是否使用相同的数据库连接同时运行两个查询?

标签: c++ sql qt pointers qlist


【解决方案1】:

我怎样才能返回一个 QList 指针并且没有内存 泄漏?

最简单也可能是最佳的方法是存储值而不是指针:

QList<Question> listOfQuestions;
QList<Answer> listOfAnswers;

另外,我们可以创建共享指针的集合。但这不是 QSharedPointer 上所读内容的预期用途。

QList<QSharedPointer<Question>> listOfQuestions;
QList<QSharedPointer<Answer>> listOfAnswers;

否则,您需要弄清楚是哪个对象负责释放作为指针传递给列表的对象(非常流行的 Qt 技术,通过提供“父”指针)。

实践中的价值观列表:

QList<Question> ExamPersistance::readQuestions(int examId)
{
    QList<Question> questions;
    QSqlQuery query(connection->getDb());
    query.prepare("select q.id, q.text from question q where id_exam =:examId");
    query.bindValue(":examId",examId);
    query.exec();
    while(query.next())
    {
        questions.append(Question(
                             query.value(1).toString(),
                                       this->readAnswers(query.value(0).toInt())));
    }
    return questions;

}

QList<Answer> ExamPersistance::readAnswers(int questionId)
{
    QList<Answer> answers;
    QSqlQuery query(connection->getDb());
    query.prepare("select a.text from answer a where id_question = :questionId");
    query.bindValue(":questionId",questionId);
    query.exec();
    while(query.next())
    {
        answers.append(Answer(query.value(0).toString()));
    }
    return answers;
}

当然要注意从您的问题中看不到的实际构造函数参数。使用 C++ 11 和“移动”构造函数应该非常有效。

【讨论】:

    【解决方案2】:

    你忘记初始化一些指针。

    修复了崩溃和内存泄漏的版本:

    QList<std::unique_ptr<Question>> ExamPersistance::readQuestions(int examId)
    {
        QList<std::unique_ptr<Question>> questions;
        QSqlQuery query(connection->getDb());
        query.prepare("select q.id, q.text from question q where id_exam =:examId");
        query.bindValue(":examId",examId);
        query.exec();
        while(query.next())
        {
            questions.append(std::make_unique<Question>(
                                 query.value(1).toString(),
                                           this->readAnswers(query.value(0).toInt())));
        }
        return questions;
    }
    

    【讨论】:

    • 我会说这是可能的,但有点超出 Qt 并且这样的唯一指针列表有点不是 C++ 标准创建者所宣扬的。
    • @AlexanderVX:不确定按值是否适用于 OP 案例,但我同意如果可能的话,QList&lt;Question&gt; 会更好。
    【解决方案3】:

    您正在取消引用 QList 指针,但您从未初始化它们(即它们指向垃圾)。这段代码应该可以解决这个问题。

    QList<Question*> *ExamPersistance::readQuestions(int examId)
    {
        QList<Question*> *questions = new QList<Question*>();
        QSqlQuery *query = new QSqlQuery(connection->getDb());
        query->prepare("select q.id, q.text from question q where id_exam =:examId");
        query->bindValue(":examId",examId);
        query->exec();
        while(query->next())
        {
            questions->append(new Question(
                                 query->value(1).toString(),
                                           this->readAnswers(query->value(0).toInt())));
            }
            return questions;
    
        }
    
    QList<Answer*> *ExamPersistance::readAnswers(int questionId)
    {
        QList<Answer*> *answers = new QList<Answer*>();
        QSqlQuery *query = new QSqlQuery(connection->getDb());
        query->prepare("select a.text from answer a where id_question = :questionId");
        query->bindValue(":questionId",questionId);
        query->exec();
        while(query->next())
        {
            answers->append(new Answer(query->value(0).toString()));
        }
       return answers;
    
    }
    

    请注意,这只是我发现的问题。即使此代码运行,它也存在其他问题。您肯定会泄漏与变量查询关联的内存,并且可能会将内存泄漏到所有问题和答案对象(以及我们使用它时的 QLists),除非您稍后在某个时间点删除它们。在 C++ 中,每次调用 new 时都需要调用 delete,否则会泄漏内存。除非您确实需要,否则在 C++ 中通常不赞成使用指针。这与必须使用 new 来创建对象的 Java 大不相同。这个answer 对 C++ 和 Java 中 new 运算符的区别进行了很好的讨论,是一个很好的起点。

    【讨论】:

    • 在堆上创建 QList 本身也会使情况变得更糟(几乎没有充分的理由)
    • @Frank 一般来说,我尝试尽可能少地更改提供的代码以提供工作示例。因为我把这个问题读作实际上是“为什么这个段错误”而不是“为什么有内存泄漏”,所以我修复了段错误,但是在这种情况下,堆上不应该有任何东西,AlexanderVX 在另一个答案中提供了一个例子
    • 啊,对不起,对,他的例子有指针。我可能不小心看到了另一个答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 2013-06-24
    • 2013-11-14
    • 2013-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多