将具有字符串成员的结构对象推入std :: vector时出错

时间:2013-03-20 05:39:55

标签: c++ string vector struct

我是C#,C程序员学习C ++,我遇到了一些麻烦。 我正在尝试将结构类型“Person”的对象推送到向量中,但不会复制作为Person类型成员的字符串值。此外,代码退出并显示错误消息 - 发布在底部:

#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>

using namespace std;

typedef struct Person {
    string Name;
    string Lastname;
    int Age;
} Person;

void CreatePerson(Person* in_person, string in_name, string in_last,
    int in_age) 
{
    Person t_person;
    t_person.Name = in_name;
    t_person.Lastname = in_last;
    t_person.Age = in_age;

    memcpy(in_person, &t_person, sizeof(t_person));
}

int main(int argc, char *argv[]) 
{
    vector<Person> people;
    Person t_ppl;

    CreatePerson(&t_ppl, "Zareh", "Petros", 13);
    people.push_back(t_ppl);

    CreatePerson(&t_ppl, "Tina", "Yarroos", 26);
    people.push_back(t_ppl);

    int ii;
    for(ii=0; ii < people.size() ; ii++) {
        cout << "Element - " << ii << endl;
        cout << "name:" << people[ii].Name << endl;
        cout << "lastname:" << people[ii].Lastname << endl;
        cout << "age:" << people[ii].Age << endl;
    }

    return 0;
}

以下是错误消息:

*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x09d48048 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75ee2)[0xb74e8ee2]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0xb76e551f]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSs4_Rep10_M_destroyERKSaIcE+0x1b)[0xb76cc99b]
/usr/lib/i386-linux-gnu/libstdc++.so.6(+0x909dc)[0xb76cc9dc]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSsD1Ev+0x2e)[0xb76cca4e]

3 个答案:

答案 0 :(得分:3)

std :: string是一个类,不应该通过memcpy操作进行复制。它可能包含特定于实例的数据,如果由两个不同的实例持有,这些数据将被加扰(这可能是导致问题的原因)。

想象一下,std :: string类似于:

class string
{
private:
    char * data;
    int dataLength;
};

如果你将一个字符串memcpy到另一个字符串,data和dataLength都会被复制到另一个地方(最近被视为普通的字符串实例)。但是,当在这些字符串上调用析构函数时(当它们超出范围时),它们将尝试释放data字段中保存的数据。第一个字符串(它是该指针的实际所有者)将释放该指针指向的内存。但是,你复制的字符串的另一个析构函数将运行并将尝试再次释放此内存,这是不允许的。

请注意,这正是您的系统报告的内容:双重释放内存。

您的代码非常C风格。在C ++中,可以使用构造函数而不是函数创建一个类,该函数填充结构。我会用以下方式编写代码:

#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>

using namespace std;

struct Person 
{
public:
    string Name;
    string Lastname;
    int Age;

    Person(string newName, string newLastname, int newAge)
        : Name(newName), Lastname(newLastname), Age(newAge)
    {

    }
};

int main(int argc, char *argv[]) 
{
    vector<Person> people;

    Person person1("Zareh", "Petros", 13);
    people.push_back(person1);

    Person person2("Tina", "Yarros", 26);
    people.push_back(person2);

    for(unsigned int i=0; i < people.size() ; i++) 
    {
        cout << "Element - " << i << endl;
        cout << "name:" << people[i].Name << endl;
        cout << "lastname:" << people[i].Lastname << endl;
        cout << "age:" << people[i].Age << endl;
    }

    getchar();

    return 0;
}

您的创建方法的角色采用类构造函数。它恰当地填补了班级的领域。此外,C ++提供了默认的复制构造函数和赋值运算符,它将处理一个人正确分配给另一个人。


关于代码风格的一些注意事项。

  • 在C ++中避免使用memcpy。如果您需要使用它,您可能应该考虑创建正确的复制构造函数,std :: copy或简单地进行赋值(在您的情况下哪些方法可以完美地工作)。复制原始内存块时,memcpy应仅用
  • C ++中的结构不再需要typedef。而不是写:

    typedef struct Name { ... } Name;
    

    您可以简单地写一下:

    struct Name { ... };
    

答案 1 :(得分:2)

不要在非POD类型上使用memcpy()。它不会调用copy-constructors。

改为使用std::copy()

在这种情况下,更容易进行分配。替换:

memcpy(in_person, &t_person, sizeof(t_person));

*in_person = t_person;

答案 2 :(得分:2)

你已经被告知在这种情况下你不应该使用memcpy,所以我不会再重复这个了。

CreatePerson的问题远远超出使用memcpy的问题,而只是更改为std::copy并不能真正做到正确。

而不是创建一个人的自由函数,你几乎肯定会把这个功能写成构造函数:

struct Person {
    string Name;
    string Lastname;
    int Age;

    Person(string Name, string Last, int Age) 
       : Name(Name), LastName(Last), Age(Age) 
    {}
};

有了这个,我们可以更干净地创建Person对象:

std:::vector<Person> people;

people.push_back(Person("Zarah", "Petros", 13));
people.push_back(Person("Tina", "Yarroos", 26));

我还会写一个插件,负责以正确的格式显示Person

std::ostream &operator<<(std::ostream &os, Person const &p) { 
    return os << "Name: " << p.Name < "\n"
              << "Last: " << p.LastName << "\n"
              << "Age:  " << p.Age << "\n";
}

有了这个,您的主流代码可以将完整的Person对象插入到流中,而无需关注Person包含的内容或其显示方式的内部详细信息:

for (int i=0; i<people.size(); i++)
    std::cout << people[i] << "\n";

如果您想要更加雄心勃勃,可以使用标准算法:

std:copy(people.begin(), people.end(), 
         std::ostream_iterator<Person>(std::cout, "\n"));

或者,如果您使用的是相对较新的编译器,则可以使用基于范围的for循环:

for (auto &p : people)
    std::cout << p << "\n";

把所有这些放在一起,你的完整程序最终会是这样的:

#include <string>
#include <iostream>
#include <vector>

using std::string;

struct Person {
    string Name;
    string LastName;
    int Age;

    Person(string Name, string Last, int Age) 
       : Name(Name), LastName(Last), Age(Age) 
    {}
};

std::ostream &operator<<(std::ostream &os, Person const &p) { 
    return os << "Name: " << p.Name << "\n"
              << "Last: " << p.LastName << "\n"
              << "Age:  " << p.Age << "\n";
}

int main(){ 
    std::vector<Person> people;

    people.push_back(Person("Zarah", "Petros", 13));
    people.push_back(Person("Tina", "Yarroos", 26));

    for (auto &p : people)
        std::cout << p << "\n";
    return 0;
}