0

First of all, I'm relatively new to C and C++, I'm way more used to 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.

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

Here is the CTe and the NFe classes:

NFE

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

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();
};
Renan Klehm
  • 158
  • 1
  • 1
  • 9
  • Unfortunately, because your question does not meet requirements for a [mre], as explained in the [help], it is unlikely that anyone will be able to help you. You will have to use your debugger to run your program one line at a time, inspect the values of all variables, as they change, and observe your program's logical execution flow, in order to analyze why your program is not producing the correct results. – Sam Varshavchik Nov 20 '19 at 02:13
  • Are you sure you have your `push_back` in the right place? It feels like it should probably be in the inner most loop, right? – Tas Nov 20 '19 at 02:19
  • 1
    Why don't you use std::string instead of char * inside the class definition? I see a problem with temp.cntr = targetCNTR.c_str(). targetCNTR is a temporary string copy which gets destroyed after the for loop. I am not sure if the character pointer returned via c_str() is valid after the string is destroyed (most probably not) – Yasir Khan Nov 20 '19 at 02:33
  • 1
    Yep, it is discussed here, the c_str() returned pointer becomes invalid as soon as the corresponding string object gets destroyed. see https://stackoverflow.com/questions/6456359/what-is-stdstringc-str-lifetime – Yasir Khan Nov 20 '19 at 02:38

1 Answers1

1

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

temp.cntr = targetCNTR.c_str();

is invalid as soon as the targetCNTR gets destroyed after exiting the scope.

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

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

This way, the returned c_str() will be valid as long as listElement vector is not destroyed or modified. 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

/* 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. If you use this approach, remember to free the char * in the class destructor. 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;
        }

};
Evg
  • 25,259
  • 5
  • 41
  • 83
Yasir Khan
  • 645
  • 4
  • 9