qt錯誤記錄:QMap等容器與 RtlWerpReportException 錯誤

好久好久沒寫博客了……自從上次記錄錯誤問題之後到現在,遇到的問題都通過百度等方式得到了解決,就懶得記錄了。但這次出現的問題又是在網上搜了好久都沒有解決的,現在終於解決了,於是記錄下來。

問題的前因後果如下:

近期工作要求,我從年後開始用Qt,之前一直用的是VS,在Qt方面我就是個初學者,現在邊學邊用了兩個月吧(年後復工開始正式用Qt)。但因爲我比較熟悉C/C++,對於Qt我上手很快,算下來工作進度也還可以。

但最近出了個問題,我編譯項目的時候沒問題,但運行的時候就會報錯:

RtlWerpReportException failed with status code :-1073741823. Will try to launch the process directly

我通過百度,得知該問題的本質是訪問到非法的地址,導致該問題出現的原因有下標越界、訪問了未分配的空間等,網上給出了很多解決方法,但沒有跟我這裏出現一樣情況的(也有可能我太菜了,犯的是很幼稚的錯誤)。因爲網上的解決方案很容易搜出來,具體哪些方法就不列了。

下面說一下我寫的程序的情況。

我用的是Qt5.7.1,MSVC-2013版,工具是QtCreator,編譯方式debug。項目用幾個.pri文件分爲了幾部分,其中有一個存放數據的部分,裏面的一個文件定義了一些類,類的結構大體是這樣的(下面的內容是把問題關鍵簡化後的內容,之後出現的代碼也如此,就不特意說明了):

/* Data.h */
// 所有類都定義和實現了構造、析構函數

class A
{
private:
    QString a_str;
public:
    QString GetAStr();    // 獲取a_str
    void PrintA();        // 打印類A的內容
};

class B
{
private:
    QString b_str;
    QMap<QString, A*> b_map;
public:
    QString GetBStr();            // 獲取b_str
    QMap<QString, A*> GetBMap();  // 獲取b_map
    void PrintB();                // 打印類B的內容
};

/////////////////////////////////////////////////

/* Data.cpp */

// ...

QString A::GetAStr()
{
    return a_str;    // 1
//    QString temp_a = a_str;    // 2
//    return temp_a;    // 3
}

// ... 其他Get函數
// ...

#define Qstr2c(qstring) qstring.toStdString().c_str()

void A::PrintA()
{
    qDebug("A:%s",QStr2c(a_str));
}

void B::PrintB()
{
    qDebug("B:%s",QStr2c(b_str));
    auto it = b_map.begin();
    while(it != b_map.end())
    {
        it.value().PrintA();
        ++it;
    }
}

// ...

在另一個pri下的文件中,有使用這些類的代碼:

/* UseData.cpp */

#include "Data.h"

void fun1(B *b)
{
    QString b_str = b->GetBStr();    // 1
    QMap<QString,A*>::iterator it = b->GetBMap().begin();    // 2
    while(it != b->GetBMap().end())    // 3
    {
        if(NULL == it.value()) break;    // 4
        QString a_str = it.value().GetAStr();    // 5
//        it.value().PrintA();    // 6
    }
}

void fun2(B *b)
{
    b->PrintA();    // 1
}

void fun()
{
    B *b = NULL;
    // ... 其他地方已生成類B,且已在b_map分配多個類A的空間和填充數據
    // ... 將有數據的類B指針傳給b
    fun1(b);    // fun2(b);
}

其中fun1()是獲取類A、B指針並使用它們的函數;fun2()打印類A、B內容的函數。我在fun()函數中調用fun1(),會出現“RtlWerpReportException”問題,通過調試,定位到fun1()函數的第5行,之後定位到A::GetAStr()函數的第1行,於是我將A::GetAStr()函數的第1行替換成第2、3行的內容,錯誤出現在了第2行

一開始我懷疑是沒有把內容傳進去的原因,就把fun1()函數的第5行替換成第6行,仍然報同樣的錯誤,問題定位在類A的Print()函數中對成員a_str操作的部分;我又在fun()函數中把fun1()函數替換成fun2()函數,結果沒問題了,調試信息顯示如下:

B:[b_str字符串的內容]

A:[第1個a_str字符串的內容]

A:[第2個a_str字符串的內容]

...

內容完全沒問題,說明兩個類的數據正確的存進去了。

那麼問題出在哪裏呢?同樣是使用了類A的a_str變量,爲什麼有的時候沒問題,有的時候就有訪問地址出錯的問題呢,一定是它們之間有什麼不同的地方,導致了問題的發生。

經過比較和思考,我感覺可能跟模塊訪問有關,他們一個是在自己的模塊中訪問和使用的,一個是在其他模塊訪問和使用的, 這裏面還牽涉到了指針,有可能在一個模塊指針指向的地址和另一個模塊是不能通用的。我之前在C++編寫和使用Dll的時候出現過模塊方面的問題,這裏有沒有可能是類似的情況?

於是我在QtCreator的“幫助”搜Module,找到了一篇標題爲《Thread-Support in Qt Modules》的文檔,在其中的Threads and Implicitly Shared Classes”部分有一段話:

Qt uses an optimization called implicit sharing for many of its value class, notably QImage and QString. Beginning with Qt 4, implicit shared classes can safely be copied across threads, like any other value classes. They are fully reentrant. The implicit sharing is really implicit.

這裏牽涉出了一個“隱式共享”的概念,大體意思是從Qt4開始,它在許多類使用了“隱式共享”,隱式共享的類能夠安全地在不同線程之間拷貝等等。關於隱式共享的說明能從網上搜到不少,有一些挺詳細的,這裏就不多贅述。

切換到《Implicit Sharing》標題的文檔,有一個List of Classes”條目,裏面列出了隱式共享的類,包括了QString、QVector、QMap等,還有一句警告:

Warning: Be careful with copying an implicitly shared container (QMap, QVector, etc.) while you use STL-style iterator. See Implicit sharing iterator problem.

 看到這裏,我有了個猜測,也許與我用了STL風格的容器有關係?

轉到“STL-Style Iterators”部分,有這樣的說明:

Thanks to implicit sharing, it is very inexpensive for a function to return a container per value. The Qt API contains dozens of functions that return a QList or QStringList per value (e.g., QSplitter::sizes()). If you want to iterate over these using an STL iterator, you should always take a copy of the container and iterate over the copy.

這裏給出了一個使用StL迭代器的例子:

  // RIGHT
  const QList<int> sizes = splitter->sizes();
  QList<int>::const_iterator i;
  for (i = sizes.begin(); i != sizes.end(); ++i)
      ...

  // WRONG
  QList<int>::const_iterator i;
  for (i = splitter->sizes().begin();
          i != splitter->sizes().end(); ++i)
      ...

參考這個例子,我修改了代碼,將fun1()函數改成了下面的樣子:

void fun1(B *b)
{
    QString b_str = b->GetBStr();    // 1
//    QMap<QString,A*>::iterator it = b->GetBMap().begin();    // 2
//    while(it != b->GetBMap().end())    // 3
    QMap<QString,A*> temp = b->GetBMap();    // 改1
    QMap<QString,A*>::iterator it = temp.begin();    // 改2
    while(it != temp.end())    // 改3
    {
        if(NULL == it.value()) break;    // 4
        QString a_str = it.value().GetAStr();    // 5
    }
}

問題解決^ ^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章