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

C++ std::vector push_back with strange behaviour

首先,我对 C 和 C++ 比较陌生,我更习惯 C#。

基本上,我有一个名为 CTe 的对象向量,这个对象有几个变量和一个名为 NFe 的另一个对象的向量,我想要填充 CTes 对象的所有信息都存储在一个二维字符串向量中,所以这个循环的目标是遍历向量的所有行,每行一个 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;
        }
    }

问题是,ctes 向量中的所有 CTe 对象都是相同的,唯一起作用的是 CTe 中的 NFe 向量。

这是 CTe 和 NFe 类:

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();
};

正如我之前在评论中提到的,c_str()返回的char *只有在相应的字符串对象被销毁或修改之前才有效。在你的情况下,

string targetCNTR = listElement[i][6];

在 vector 中创建一个字符串的副本,这个副本在退出 for 循环后立即销毁。所以

temp.cntr = targetCNTR.c_str();

一旦 targetCNTR 在退出作用域后被销毁就无效。

如果您无法修改 class 定义以将 const char * 更改为 std::string,并且如果向量 listElement 在程序的整个生命周期内保持有效,您可以修复通过获取对实际字符串的引用来编写代码,如下所示

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

这样,只要listElement向量没有被破坏或修改,返回的c_str()就会有效。如果 istElement 是 modified/destroyed(您没有提供填充 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; 

您可以对所有 char * 个变量执行相同的操作。如果您使用这种方法,请记住在 class 析构函数中释放 char *。例如,

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

};