简体   繁体   English

C++ std::vector push_back 具有奇怪的行为

[英]C++ std::vector push_back with strange behaviour

First of all, I'm relatively new to C and C++, I'm way more used to C#.首先,我对 C 和 C++ 比较陌生,我更习惯于 C#。

Basically, I have a vector an object called CTe, this object has several variables and a vector of another object called NFe, all the information that I want to fill the CTes objects are stored in a 2D vector of strings, so the goal of this loop is to iterate through all the lines of the vector, being one CTe per line, and this CTe may have one or multiples NFes attached to it.基本上,我有一个向量 object,称为 CTe,这个 object 有几个变量和另一个 object 的向量,称为 NFe,我要填充 CTes 对象的所有信息都存储在这个字符串的 2D 向量中循环是遍历向量的所有行,每行一个CTe,这个CTe可能有一个或多个NFes附加到它上面。

    /*The declaration of the vectors, just to help visualize the structure
    vector < string > dirlist;
    vector < vector < string > > listElement;
    vector < CTe > ctes;*/

    //This will iterate through the 2D vector
    for (int i = 1; i < listElement.size(); i++)
    {
        //Temporally CTe object to store information
        CTe temp;

        string targetCNTR = listElement[i][6];
        int countNFE = 0;
        bool haveFoundReference = false;

        // This will iterate reading all the xmls I have in a folder
        for (int j = 2; j < dirlist.size(); j++)
        {
            string extraInfoNFE = ReadXML(dirlist[j], "infAdic", "infCpl");

            //Check if a valid xml was found.
            if (extraInfoNFE.find(targetCNTR) != string::npos)
            {
                haveFoundReference = true;

                string sColeta = "000"+listElement[i][3];
                stringstream tipoNumber (listElement[i][4]);

                //Fill the variables of the temp object
                temp.cntr = targetCNTR.c_str();
                temp.booking = listElement[i][0].c_str();
                temp.motorista = listElement[i][1].c_str();
                temp.placas = listElement[i][2].c_str();
                temp.coleta = sColeta.c_str();
                temp.seqEndereco = listElement[i][5].c_str();
                tipoNumber >> temp.tipoCTE;

                listElement[i][8+countNFE] = ReadXML(dirlist[j], "ide", "nNF");

                //Create a temporally object to store the NFe information
                NFe tempNFe;

                //Fill the tempNFe object
                stringstream nfeNumber (listElement[i][8+countNFE]);
                nfeNumber >> tempNFe.numeroNFE;

                string sXML = dirlist[j].substr(5, 43);
                tempNFe.codigoXML = sXML.c_str();

                string sDest = ReadXML(dirlist[j], "dest", "xNome");
                tempNFe.destinatario = sDest.c_str();

                stringstream cfopNumber (ReadXML(dirlist[j], "det", "prod", "CFOP"));
                cfopNumber >> tempNFe.cfop;

                stringstream qtdeNumber (ReadXML(dirlist[j], "transp", "vol", "qVol"));
                qtdeNumber >> tempNFe.qtde;

                stringstream valorNumber (ReadXML(dirlist[j], "total", "ICMSTot", "vNF"));
                valorNumber >> tempNFe.valor;

                stringstream pesoNumber (ReadXML(dirlist[j], "transp", "vol", "pesoB"));
                pesoNumber >> tempNFe.pesoBruto;

                //push_back the tempNFe into the temp object
                //This part is working perfectly
                temp.notas.push_back(tempNFe);
                countNFE++;
            }
        }

        //Check if a valid xml was found, if so push_back
        //The temp object into the ctes object
        //HERE LIES THE PROBLEM
        if (haveFoundReference)
        {
            cout<<temp.cntr<<" - ";
            ctes.push_back(temp);
        }
        else
        {
            cout << "Não foi possível localizar a nota do CNTR " <<targetCNTR;
        }
    }

The problem is, that all the CTe objects in the ctes vector are the same, the only thing that is working is the NFe vector inside the CTe.问题是,ctes 向量中的所有 CTe 对象都是相同的,唯一有效的是 CTe 内的 NFe 向量。

Here is the CTe and the NFe classes:这是 CTe 和 NFe 类:

NFE NFE

class NFe
{
    public:
        NFe();
        const char* codigoXML;
        const char* destinatario;
        int numeroNFE;
        int cfop;
        int qtde;
        float valor;
        float pesoBruto;
        ~NFe();
};

CTE CTE

#include <nfe.h>
#include <vector>

class CTe
{
    public:
        CTe();
        const char* motorista;
        const char* placas;
        const char* booking;
        const char* cntr;
        const char* seqEndereco;
        const char* coleta;
        int espelho;
        int tipoCTE;
        std::vector < NFe > notas;
        virtual ~CTe();
};

As I mentioned in the comments earlier, the char * returned by c_str() is valid only until the corresponding string object is destroyed or modified.正如我在前面的评论中提到的, c_str()返回的char *仅在相应的字符串 object 被销毁或修改之前有效。 In your case,在你的情况下,

string targetCNTR = listElement[i][6];

creates a copy of the string inside the vector, and this copy is destroyed right after exiting the for loop.在向量内创建字符串的副本,并且该副本在退出 for 循环后立即被销毁。 So所以

temp.cntr = targetCNTR.c_str();

is invalid as soon as the targetCNTR gets destroyed after exiting the scope.退出 scope 后targetCNTR被破坏后立即无效。

If you cannot modify the class definition to change const char * to std::string and if the vector listElement remains valid throughout the lifetime of your program, you can fix your code by obtaining the reference to the actual string like below如果您无法修改 class 定义以将const char *更改为std::string并且如果向量listElement在程序的整个生命周期内保持有效,则可以通过获取对实际字符串的引用来修复代码,如下所示

string &targetCNTR = listElement[i][6];

This way, the returned c_str() will be valid as long as listElement vector is not destroyed or modified.这样,只要listElement向量未被破坏或修改,返回的c_str()将是有效的。 In case istElement is modified/destroyed (you haven't provided the code populating listElement so it is not obvious), you can also create the copy of char * like this如果istElement被修改/销毁(您没有提供填充listElement的代码,因此不明显),您还可以像这样创建char *的副本

/* allocate a new char * on heap */
char *cntr = new char [strlen(targetCNTR.c_str()) + 1];
/* copy the c string */
strcpy(cntr, targetCNTR.c_str());
/* move the char * to desired object */
temp.cntr = cntr; 

You can do the same for all char * variables.您可以对所有char *变量执行相同的操作。 If you use this approach, remember to free the char * in the class destructor.如果您使用这种方法,请记住释放 class 析构函数中的char * For example,例如,

class CTe
{
    public:
        CTe();
        const char* motorista;
        const char* placas;
        const char* booking;
        const char* cntr;
        const char* seqEndereco;
        const char* coleta;
        int espelho;
        int tipoCTE;
        std::vector < NFe > notas;
        virtual ~CTe() {
            /* free char * */
            delete [] motorista;
            delete [] placas;
            delete [] booking;
            delete [] cntr;
            delete [] seqEndereco;
            delete [] coleta;
        }

};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM