You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++多态类元素删除异常:调用析构函数误删全部基类元素

C++继承体系元素删除错误与析构、抽象类用法修正

问题说明

我编写的C++程序中,定义了Persoana基类及其两个派生类Student和Angajat。菜单选项3中,程序要求输入姓名删除对应元素,但当前实现会误删所有相关元素,不清楚如何正确使用析构函数和抽象类。

原代码

#include <iostream>
#include <string>
#include <conio.h>
#include <functional>

using namespace std;

class Persoana {
public:
    int varsta;
    string nume, prenume;
    //constructor vid cu scopu pentru declararea vectorului
    Persoana() {}
    Persoana(string nume, string prenume, int age)
    {
        this->nume = nume;
        this->prenume = prenume;
        varsta = age;
    }
    virtual void afisare()
    {
        cout << "\n";
        cout << "Numele: " << nume << "\n";
        cout << "Prenumele: " << prenume << "\n";
        cout << "Varsta: " << Persoana::varsta << "\n";
    }
    string returnName()
    {
        return nume;
    }

    ~Persoana() {};

};

class Angajat :public Persoana {
public:
    string departament;
    int salariu;
    Angajat() {}
    Angajat(string nume, string prenume, int varsta, string departament, int salariu)
        :Persoana(nume, prenume, varsta)
    {
        this->departament = departament;
        this->salariu = salariu;
    }
    virtual void afisare()
    {
        Persoana::afisare();
        cout << "Departament: " << departament << endl;
        cout << "Salariu: " << salariu << endl;
    }
    //Mai jos este sunt 3 "gathers" , care returneaza nume, prenume, varsta
    string returnNume() {
        return nume;
    }

    string returnPrenume() {
        return prenume;
    }

    int returnVarsta()
    {
        return varsta;
    }

};

class Student : public Persoana {
public:
    string tip;
    int an;
    Student() {}
    Student(string nume, string prenume, int varsta, string tip, int an)
        :Persoana(nume, prenume, varsta)
    {
        this->tip = tip;
        this->an = an;
    }

    string returnNume()
    {
        return nume;
    }

    string returnPrenume()
    {
        return prenume;
    }
    int returnAge()
    {
        return varsta;
    }

    virtual void afisare() {
        Persoana::afisare();
        cout << "Tip:" << tip << endl;
        cout << "Varsta:" << varsta << endl;
    }
    
};

void menu()
{
    string n, p, d;
    int i_2=0,v, s, i=0, ok = 0, ok2 = 0, nr=0 , opt, nr2 = 0,nr3=0;
    string num, num2;
    Student student[40];
    Angajat angajat[40];
    Persoana * persoane[80];
    int choice;
    do
    {
        cout << "0.Adaugare student" << endl;
        cout << "1.Adaugare angajat" << endl;
        cout << "2. Cautare" << endl;
        cout << "3. Stergere" << endl;
        cout << "4. Exit" << endl;
        cin >> choice;
        switch (choice)
        {
        case 0:
            
            nr++;
            cout << "Dati numele: ";
            cin >> n;
            cout << "Dati prenumele: ";
            cin >> p;
            cout << "Dati varsta: ";
            cin >> v;
            cout << "Dati tip: ";
            cin >> d;
            cout << "Dati an:";
            cin >> s;   
            student[nr2] = Student(n, p, v, d, s);
            persoane[i] = &student[nr2];
            nr2 = nr2 + 1;
            i = i + 1;
                for (i = 0; i < nr; i++)
                {
                    persoane[i]->afisare();
                }
            break;
        case 1:
            
            //retin numaru de studenti si angajati(in total) prin acest nr
            nr++;
            cout << "Dati numele: ";
            cin >> n;
            cout << "Dati prenumele: ";
            cin >> p;
            cout << "Dati varsta: ";
            cin >> v;
            cout << "Dati departamentul: ";
            cin >> d;
            cout << "Dati salariul:";
            cin >> s;
            
            angajat[nr3] = Angajat(n, p, v, d, s);
            persoane[i] = &angajat[nr3];
            nr3++;
            i++;
            break;
        case 2:
                       //this search is working perfectly fine :)
            cout << "Introduceti numele persoanei: ";
            cin >> num;
            ok = 0;
            
            
            for (i = 0; i < nr; i++)
            {
                
                if ((num.compare(persoane[i]->returnName())) == 0)
                {
                    persoane[i]->afisare();
                }
                ok++;
            }
            if (ok == 0)
                cout << "Aceasta persoana nu se afla in institutie:(";
            break;
        case 3:
            cout << "Introduceti numele pe care vreti sa il ste/rgeti:";
            cin >> num2;
            //here i want to delete an element, but when this is running will delete everything from my class:(
            for (i = 0; i < nr; i++)
            {
                if (num2.compare(persoane[i]->returnName()) == 0)
                {
                    persoane[i]->~Persoana(); 
                    nr--;
                    ok2++;
                }
            }
            if (ok2 == 0)
            {
                cout << "Element not found";
            }
            else
            {
                cout << "Elementul a fost sters:";
            }
            break;
        case 4:         
            exit(0);
        }
    } while (choice != 4);
}


int main()
{
    menu();
}

问题根源分析

  1. 手动调用析构函数错误:persoane[i]->~Persoana()是错误操作,因为student和angajat数组是栈上分配的局部对象,手动调用析构函数后,对象内存会在函数结束时被自动释放,导致双重释放,且并未从persoane数组中移除该元素的引用。
  2. 基类析构函数非虚:Persoana的析构函数不是虚函数,通过基类指针操作派生类对象时,无法正确调用派生类析构函数,违反多态规范(即使当前类无动态资源,这也是必要的)。
  3. 删除逻辑缺陷:找到匹配元素后仅减少nr,未调整persoane数组的元素顺序,后续遍历仍会访问到无效的对象引用。
  4. 未正确使用抽象类:Persoana作为基类不应被实例化,需将afisare()设为纯虚函数使其成为抽象类。

修正方案

1. 将Persoana改为抽象类并添加虚析构函数

修改Persoana类,把afisare()声明为纯虚函数,同时将析构函数设为虚函数:

class Persoana {
public:
    int varsta;
    string nume, prenume;
    Persoana() {}
    Persoana(string nume, string prenume, int age)
        : nume(nume), prenume(prenume), varsta(age) {}
    // 纯虚函数,使Persoana成为抽象类,禁止实例化
    virtual void afisare() = 0;
    string returnName() {
        return nume;
    }
    // 虚析构函数,确保派生类析构被正确调用
    virtual ~Persoana() {};
};

2. 修复删除逻辑

不要手动调用析构函数,而是通过调整persoane数组的元素位置来移除目标对象的引用:

case 3:
    cout << "Introduceti numele pe care vreti sa il stergeti:";
    cin >> num2;
    ok2 = 0;
    for (i = 0; i < nr; ) {
        if (num2.compare(persoane[i]->returnName()) == 0) {
            // 将后续元素前移,覆盖当前要删除的元素引用
            for (int j = i; j < nr - 1; j++) {
                persoane[j] = persoane[j + 1];
            }
            nr--;
            ok2++;
            // 元素前移后,当前位置需重新检查,不执行i++
        } else {
            i++;
        }
    }
    if (ok2 == 0) {
        cout << "Element not found";
    } else {
        cout << "Elementul a fost sters:";
    }
    break;

3. 修复循环变量冲突

原代码case 0中重新赋值循环变量i会导致后续逻辑混乱,修改循环变量名称:

// case 0中的输出循环
for (int k = 0; k < nr; k++) {
    persoane[k]->afisare();
}

4. 移除冗余代码

派生类中returnNume、returnPrenume等函数与基类功能重复,可直接删除,使用基类的returnName或直接访问公有成员。

内容的提问来源于stack exchange,提问作者Gigea Nicusor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:50:37