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(); }
问题根源分析
- 手动调用析构函数错误:
persoane[i]->~Persoana()是错误操作,因为student和angajat数组是栈上分配的局部对象,手动调用析构函数后,对象内存会在函数结束时被自动释放,导致双重释放,且并未从persoane数组中移除该元素的引用。 - 基类析构函数非虚:
Persoana的析构函数不是虚函数,通过基类指针操作派生类对象时,无法正确调用派生类析构函数,违反多态规范(即使当前类无动态资源,这也是必要的)。 - 删除逻辑缺陷:找到匹配元素后仅减少
nr,未调整persoane数组的元素顺序,后续遍历仍会访问到无效的对象引用。 - 未正确使用抽象类:
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
相关产品推荐
相关产品推荐

