0

我是一名 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]
4

3 回答 3

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 { ... };
    
于 2013-03-20T05:48:42.123 回答
2

不要memcpy()在非 POD 类型上使用。它不调用复制构造函数。

改为使用std::copy()

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

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

*in_person = t_person;
于 2013-03-20T05:42:25.330 回答
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正确的格式显示 a:

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

有了这个,你的主流代码可以将完整Person的对象插入到流中,而无需关注 aPerson包含的内容或它应该如何显示的内部细节:

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;
}
于 2013-03-20T05:56:15.570 回答