【问题标题】:Dynamic allocation of classes类的动态分配
【发布时间】:2018-11-18 23:29:13
【问题描述】:

我有一个关于对象数组分配的问题, 问题是这样的: 我有一个班级叫人:

class Person {
    char* name;
    int ID;


public:
    Person(char* name = NULL, int ID = 0) :name(name), ID(ID) {};
}

我正在尝试制作一组​​这样的人:

Person *pArr = new Person[size];

然后我从文件中提取数据(char* 表示字符串,int 表示 ID) 我使用 for 循环和构造函数将人员放置在循环中,如下所示:

for (int j = 0; j < size3; j++) {
pArr[j] = Person(Name, id);
}

在我完成我的程序后,我想使用析构函数并释放存储在 char* 名称中的分配字符串,但是当我添加析构函数时,它会在循环结束后立即触发, 每个被创建的 Person 都会立即销毁, 我知道它可以创建一个指针数组并分配这个人,但是在这个任务中我想这样做, 有没有正确的方法可以在不立即触发析构函数的情况下做到这一点?

【问题讨论】:

  • 使用std::vectorstd::string 代替数组,你的生活会更轻松。
  • 拥有char * 作为类成员的原因很少。使用std::string
  • 您可能需要为您的班级定义一个赋值运算符。关注Rule of 3 and 5
  • pArr[j] = Person(Name, id); 创建一个临时的Person 对象,将其值复制到pArr[j],然后销毁临时的Person 对象。
  • @samuelnj 因为不允许 OP 使用 std::stringstd::vector,他可能也不允许使用 std::unique_ptr

标签: c++ arrays class


【解决方案1】:

在这一行:

pArr[j] = Person(Name, id);

您正在创建一个 临时 Person 对象,然后将其分配给 pArr[j] 对象。 Person 没有定义显式的复制赋值运算符,因此编译器会为您自动生成一个,它只是将值从一个对象逐个成员复制到另一个对象。

当临时对象超出; 的范围时,它会被自动销毁。编译器生成的赋值运算符将name 指针原样复制到pArr[j] 对象,因此如果Person 有一个释放其name 的析构函数,则pArr[j] 对象将留下一个悬空的@987654332 @指向已释放内存的指针。

您的Person 课程未遵循Rule of Three

三法则(也称为三巨头或三巨头)是 C++(C++11 之前)中的一条经验法则,它声称如果一个类定义了一个(或多个)以下它可能应该明确定义所有三个:

  • 析构函数
  • 复制构造函数
  • 复制赋值运算符

由于您希望Person 有一个释放name 的析构函数,它还需要一个复制构造函数和一个复制赋值运算符,以便它可以复制name 以释放析构函数,例如:

class Person
{
    char* name;
    int id;

public:
    Person(const char* Name = NULL, int ID = 0)
        : name(new char[std::strlen(Name)+1]), id(ID)
    {
        std::strcpy(name, Name);
    }

    Person(const Person &src)
        : name(new char[std::strlen(src.name)+1]), id(src.id)
    {
        std::strcpy(name, src.name);
    }

    ~Person()
    {
        delete[] name;
    }

    Person& operator=(const Person &rhs)
    {
        if (&rhs != this)
        {
            Person tmp(rhs);

            //std::swap(tmp.name, name);
            char *p = tmp.name;
            tmp.name = name;
            name = p;

            id = tmp.id;
        }
        return *this;
    }
};

现在,回到这一行:

pArr[j] = Person(Name, id);

临时文件将安全正确地复制到pArr[j]。请注意,如果您事先动态分配了Name,那么您现在必须在之后释放它,因为Person 会制作自己的内部副本。


如果您可以将char* 更改为std::string,那么您不必担心编译器会生成默认副本,因为std::string 符合三规则并且会被正确复制:

class Person
{
    std::string name;
    int id;

public:
    Person(const std::string &name = std::string(), int ID = 0)
        : name(name), id(ID)
    {
    }

    // no explicit copy constructor, destructor, or
    // assignment operator is needed, as compiler-generated
    // defaults will suffice...
};

【讨论】:

  • 嗨,我按照你的建议做了操作员,分配了新内存,但在离开循环后它仍然立即触发析构函数删除我的名字..
  • @AlexAlex 应该是这样,因为循环正在创建临时的 Person 对象,这些对象在每次分配后立即超出范围。你无法避免这一点。但是,通过为Person 的每个实例赋予它自己的name 数据副本,临时对象被销毁就不再重要了。只有 temp 的析构函数被调用,所以只有 temp 的数据被释放。由于分配了额外的数据副本,因此保留了其他所有人的数据。你最初没有这样做
猜你喜欢
  • 2020-09-03
  • 1970-01-01
  • 2021-06-05
  • 2013-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-05
  • 2013-01-13
相关资源
最近更新 更多