初始化列表中的QString导致访问冲突.这里出了什么问题?

Kni*_*chi 6 c++ qt access-violation initializer-list visual-c++

我在初始化列表中使用QString时遇到了访问冲突,我不明白.

这是一个重现问题的最小例子.

// file ClassA.h
#pragma once
#include <QString>

struct Parameter
{
    QString stringPar;
};

class ClassA
{
     QString m_string1;

public:
    void function(Parameter pars);
};
Run Code Online (Sandbox Code Playgroud)

ClassA的实施......

// file ClassA.cpp
#include "ClassA.h"

void ClassA::function(Parameter pars)
{
    m_string1 = pars.stringPar;   // last line called in my code when the crash happens
}
Run Code Online (Sandbox Code Playgroud)

和main.cpp

// file main.cpp
#include "ClassA.h"

int main()
{
    ClassA classA;

    classA.function({ QString("jkjsdghdkjhgdjufgskhdbfgskzh") });

    // when using this code the problem does not occur
    //Parameter par = { QString("jkjsdghdkjhgdjufgskhdbfgskzh") };
    //classA.function(par);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

违规时的调用堆栈:

Qt5Cored.dll!QGenericAtomicOps<QAtomicOpsBySize<4> >::load<long>(const long & _q_value) Line 96
Qt5Cored.dll!QBasicAtomicInteger<int>::load() Line 142
Qt5Cored.dll!QtPrivate::RefCount::ref() Line 57
Qt5Cored.dll!QString::operator=(const QString & other) Line 1355
EducationalCode.exe!ClassA::function(Parameter pars) Line 6
EducationalCode.exe!main() Line 8
Run Code Online (Sandbox Code Playgroud)

ClassA :: function()中的复制赋值似乎出了问题,但我不确定它是什么.当我将函数的签名更改为

function(const Parameter& pars);
Run Code Online (Sandbox Code Playgroud)

它也不会崩溃.

你有什么主意吗?

tru*_*liu 0

您应该添加一个复制构造函数:

struct Parameter
{
    QString stringPar;
    Parameter& Parameter(const Parameter& rhs)
    {
        if((void*)this == (void*)&rhs)
        {
            return *this;
        }
        this->stringPar = rhs.stringPar;
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

当您调用 ClassA::function() 时,会创建一个临时参数实例,因为 C++ 按值传递参数;像这样的东西:

void ClassA::function(Parameter pars = QString("jkjsdghdkjhgdjufgskhdbfgskzh"))
{
    m_string1 = pars.stringPar;   // last line called in my code when the crash happens
}
Run Code Online (Sandbox Code Playgroud)

如果你不编写复制构造函数,编译器将合成一个默认的复制构造函数,如下所示:

    Parameter& Parameter(const Parameter& rhs)
    {
        memcpy(this, &rhs, sizeof(Parameter));
        return *this;
    }
Run Code Online (Sandbox Code Playgroud)

我猜 QString 有指针成员,假设它的名称是 ptr。那么 pars.stringPar.ptr 和 QString("jkjsdghdkjhgdjufgskhdbfgskzh").stringPar.ptr 将指向相同的内存地址。

调用函数如下:

 classA.function({ QString("jkjsdghdkjhgdjufgskhdbfgskzh") });
Run Code Online (Sandbox Code Playgroud)

{ QString("jkjsdghdkjhgdjufgskhdbfgskzh") } 在 classA.function() 返回之前销毁对象,然后 {QString("jkjsdghdkjhgdjufgskhdbfgskzh")}.stringPar.ptr 指向的内存被释放,并且 pars.stringPar.ptr 指向无效内存。

// when using this code the problem does not occur
//Parameter par1 = { QString("jkjsdghdkjhgdjufgskhdbfgskzh") };
//classA.function(par1)
//par1 destroy when main() function return, thus classA.function() does not crash.
Run Code Online (Sandbox Code Playgroud)

请参阅《Effective C++》第 11 条:为具有动态分配内存的类声明复制构造函数和赋值运算符。有效的 C++,2E http://debian.fmi.uni-sofia.bg/~mrpaff/Effective%20C++/EC/EI11_FR.HTM