【问题标题】:Destructor called on std::vector<Class type> invalidating for_each在 std::vector<Class type> 上调用的析构函数使 for_each 无效
【发布时间】:2018-03-29 18:50:42
【问题描述】:

我一直在阅读有关delete[]new[]what-is-the-rule-of-threedeallocating-objects-stored-in-a-vector, delete-vector-class-member 遇到了一个

抛出异常:读取访问冲突。

我已经定义了一个基类

class Person
{
private:
    std::string name;
    int age;
    static int cur_id;
public:
    Person() {}
    Person(std::string new_name, int new_age) : name(new_name), age(new_age) { cur_id += 1; }
    virtual ~Person() {}
    void setname(std::string name) { this->name = name; }
    std::string getname() { return name; }
    int getage() { return age; }
    void setage(int age) { this->age = age; }
    int get_id() { return cur_id; }

    virtual void getdata(void) = 0;
    virtual void putdata(void) = 0;
};

派生类

class Professor : private Person
{
private:
    int publications;
    Professor* prof;
public:
    Professor() {}
    Professor(std::string new_name, int new_age, int new_pubs) : Person(new_name, new_age) { this->publications = new_pubs; }
    ~Professor() override { delete prof; }
    void getdata() override 
    {
        std::vector<std::string> prof_input = get_input();
        std::cout << prof_input[0] << "\n";
        std::cout << std::stoi(prof_input[1]) << "\n";
        std::cout << std::stoi(prof_input[2]) << "\n";
        prof = new Professor(prof_input[0], std::stoi(prof_input[1]), std::stoi(prof_input[2]));
    }
    void putdata() override 
    {
        std::cout << prof->getname() << " " << prof->getage() << " " << prof->publications << " " << prof->get_id();
    }
};

并且已经定义了main

int main()
{
    int size; 

    std::cin >> size;

    std::cin.ignore();

    Professor* prof = new Professor();

    std::vector<Professor> prof_vec;

    for (int i = 0; i < size; i++)
    {
        int which;
        std::cin >> which;
        std::cin.ignore();

        switch (which)
        {
        case 1: 
        {
            prof->getdata();
            prof_vec.push_back(*prof);
        }
        break;
        case 2:
        {
            // something else
        }
        break;
        }
    }

    for (auto prof : prof_vec)
    {
        prof.putdata();
    }  <<< ------------------- Things so south here

    delete prof;

    fgetc(stdin);

    return 0;
}

我想发生的事情是(假设向量中只有一个元素),一旦对putdata() 的调用退出该块,析构函数调用delete prof 就会发生

this->prof 是 0xCDCDCDCD。

我正在尝试删除一个不存在的对象。如果我想确保正确delete,即使是这样一个简单的例子,我应该怎么做才能确保没有内存泄漏?

【问题讨论】:

  • 如果你真的想以现代 C++ 方式确保没有内存泄漏,请使用smart pointer(链接很老,c++11 的智能指针被命名为std::unique_ptr)。
  • @spectras 我完全同意你的看法。我的示例是为学习正确的内存管理而设计的,但我会在心跳中使用std::unique_ptrstd::shared_ptrstd::make_unique()std::make_shared()
  • 好的。在您的程序中,for (auto prof : prof_vec) 在迭代时为每个教授制作一个临时副本。这是可能的,因为您没有禁用复制构造函数,并且您的类具有默认构造函数并且只有微不足道的非静态成员。除了编译器生成的复制构造函数只是简单地复制指针,这必然会出错。如果您不想复制它们,请使用for (auto &amp; prof : prof_vec)。并禁用该复制构造函数,您还将捕获 push_back() 副本。
  • 请阅读minimal reproducible example。这段代码的绝大部分与问题完全无关。
  • 你不应该有Professor* prof;作为教授的班级成员,这完全没有意义。相反,getdata() 函数应该初始化当前实例。

标签: c++ c++11 memory-management memory-leaks delete-operator


【解决方案1】:

你违反了rule of 3/5/0

class Professor : private Person
{
private:
    Professor* prof;
public:
    // ...
    ~Professor() override { delete prof; }
};

如果您提供特殊成员函数之一,您应该提供所有的特殊成员函数。在这种情况下,您的复制构造函数只是按成员复制Professor*,现在您有两个不同的对象试图删除同一个指针。

偏好0规则:

class Professor : private Person
{
private:
    std::unique_ptr<Professor> prof;
public:
    // ...
    ~Professor() = default;
};

【讨论】:

  • @M.M 可能是的,甚至没有注意到它们是同一类型!刚刚看到缺少特殊成员,我内心的警笛响了。
【解决方案2】:

成员Professor* prof没有在构造函数Professor()中初始化,只在getdata();中初始化

当代码转到案例 2 时,如果没有对 getdata() 的任何调用,则 prof 未初始化。

0xCDCDCDCD 表示调试模式下的未初始化值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-05
    • 2016-11-04
    • 2022-06-11
    • 2018-10-25
    相关资源
    最近更新 更多