A QString in the initialization list causes an access violation. What is wrong here?

I encountered an access violation while using QString in a list of initializers which I do not understand.

Here is a minimal example that reproduces the problem.

// file ClassA.h #pragma once #include <QString> struct Parameter { QString stringPar; }; class ClassA { QString m_string1; public: void function(Parameter pars); }; 

ClassA implementation ...

 // 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 } 

and 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; } 

Call stack during violation:

 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 

Something seems wrong with the copy assignment in ClassA :: function (), but I'm not sure what it is. When I change the function signature to

 function(const Parameter& pars); 

It also does not crash.

Do you have any ideas?

+5
source share
1 answer

You should add a copy constructor:

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

A Temp Parameter instance is created when you call ClassA :: function () due to passing C ++ arguments by value; Something like that:

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

If you did not write a copy constructor, the compiler will synthesize the default copy constructor, for example:

  Parameter& Parameter(const Parameter& rhs) { memcpy(this, &rhs, sizeof(Parameter)); return *this; } 

I assume that QString has pointer elements, considering it with the name ptr. then pars.stringPar.ptr and QString ("jkjsdghdkjhgdjufgskhdbfgskzh"). stringPar.ptr will point to the same memory address.

Call the function as follows:

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

{QString ("jkjsdghdkjhgdjufgskhdbfgskzh")} destroy the object before calling classA.function (), then the memory pointed to by {QString ("jkjsdghdkjhgdjufgskhdbfgskzh")}. stringPar.ptr is freed, and pars.stringPar.ptr points to invalid memory.

 // 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. 

See <Efficient C ++ → Clause 11: declare a copy constructor and assignment operator for classes with dynamically allocated memory. Effective C ++, 2E http://debian.fmi.uni-sofia.bg/~mrpaff/Effective%20C++/EC/EI11_FR.HTM

0
source

Source: https://habr.com/ru/post/1203819/


All Articles