C++析构函数每次输入后立即调用问题求助
问题:员工对象析构函数提前触发的原因及修复方案
问题描述
学习C++时编写员工数据录入程序,期望所有员工信息输入完成后才调用析构函数打印删除提示,但实际每输入一位员工信息后,析构函数就会立即触发。
核心问题分析
动态数组创建错误
employee *a = new employee;仅创建了单个employee对象,而非容纳n个对象的数组。后续循环中a[i]会访问超出内存范围的区域,属于未定义行为。临时对象导致提前析构
循环中a[i] = employee(no, name, position, time);会创建一个临时employee对象,完成赋值后该临时对象会被立即销毁,触发析构函数打印提示,这就是每次输入后出现删除信息的直接原因。浅拷贝引发的双重释放风险
类中包含char*指针成员,但未重载赋值运算符。默认赋值操作会直接拷贝指针地址,导致原对象和临时对象的指针指向同一块内存。临时对象析构时释放了内存,后续原对象析构会再次释放同一块内存,引发程序崩溃。其他代码问题
- 默认构造函数中
time;是无效语句,未初始化time成员; setTime方法的条件判断错误:if(a > 0 && a > 150)逻辑矛盾,应为if(a < 0 || a > 150),否则无法正确拦截无效工时。
- 默认构造函数中
修复方案及代码示例
方案1:使用setter避免临时对象+修正动态数组
直接通过成员方法为数组中的对象赋值,避免创建临时对象,同时正确初始化动态数组:
#include <iostream> #include <cstring> using namespace std; class employee{ private: int no; char *name; char *position; float time; public: employee(); employee(int a, char *n, char *m, float x); ~employee(); // setter void setNo(int); void setName(char*); void setPos(char*); void setTime(float); // getter int getNo(); char* getName(); char* getPos(); float getTime(); // 重载赋值运算符,避免浅拷贝 employee& operator=(const employee& other); }; // 实现赋值运算符重载 employee& employee::operator=(const employee& other) { if (this == &other) return *this; // 防止自赋值 // 释放当前对象的内存 delete[] name; delete[] position; // 分配新内存并拷贝数据 name = new char[strlen(other.name) + 1]; strcpy(name, other.name); position = new char[strlen(other.position) + 1]; strcpy(position, other.position); no = other.no; time = other.time; return *this; } void employee::setNo(int a){ no = a; } void employee::setName(char* a){ delete[] name; // 注意用delete[]释放数组 name = new char[strlen(a) + 1]; strcpy(name, a); } void employee::setPos(char* a){ delete[] position; position = new char[strlen(a) + 1]; strcpy(position, a); } void employee::setTime(float a){ if(a < 0 || a > 150){ // 修正条件判断 cout<<"Enter valid time! (The restriction range is between 0 to 150.)"<<endl; exit(0); } else{ time = a; } } // getter实现 int employee::getNo(){ return no; } char* employee::getName(){ return name; } char* employee::getPos(){ return position; } float employee::getTime(){ return time; } // 构造函数实现 employee::employee(){ name = new char[2]; // 至少能存空格和结束符 position = new char[9]; // "employee"是8个字符+结束符 no = 0; strcpy(name, " "); strcpy(position, "employee"); time = 0.0f; // 初始化time } employee::employee(int a, char *n, char *m, float x){ name = new char[strlen(n) + 1]; position = new char[strlen(m) + 1]; no = a; strcpy(name, n); strcpy(position, m); time = x; } employee::~employee(){ cout<<"The object "<<position<<" "<<name<<" is deleted."<<endl; delete[] name; // 数组用delete[]释放 delete[] position; } int main(){ int n; cout<<"The number of employees: "; cin>>n; employee *a = new employee[n]; // 创建n个对象的动态数组 for(int i = 0; i < n; i++){ int no; float time; char name[20], position[10]; cout<<"Enter No: "; cin>>no; cout<<"Enter Name: "; cin>>name; cout<<"Enter Position: "; cin>>position; cout<<"Enter Time: "; cin>>time; // 使用setter直接赋值,避免临时对象 a[i].setNo(no); a[i].setName(name); a[i].setPos(position); a[i].setTime(time); } // 最后手动释放数组内存,触发所有对象的析构 delete[] a; return 0; }
方案2:改用std::string简化内存管理(推荐)
C++中使用std::string可以避免手动管理内存,无需担心浅拷贝、内存泄漏或双重释放问题,代码更简洁安全:
#include <iostream> #include <string> using namespace std; class employee{ private: int no; string name; string position; float time; public: employee(); employee(int a, const string& n, const string& m, float x); ~employee(); // setter void setNo(int); void setName(const string&); void setPos(const string&); void setTime(float); // getter int getNo() const; const string& getName() const; const string& getPos() const; float getTime() const; }; void employee::setNo(int a){ no = a; } void employee::setName(const string& a){ name = a; } void employee::setPos(const string& a){ position = a; } void employee::setTime(float a){ if(a < 0 || a > 150){ cout<<"Enter valid time! (The restriction range is between 0 to 150.)"<<endl; exit(0); } else{ time = a; } } // getter实现 int employee::getNo() const{ return no; } const string& employee::getName() const{ return name; } const string& employee::getPos() const{ return position; } float employee::getTime() const{ return time; } // 构造函数实现 employee::employee() : no(0), name(" "), position("employee"), time(0.0f) {} employee::employee(int a, const string& n, const string& m, float x) : no(a), name(n), position(m), time(x) {} employee::~employee(){ cout<<"The object "<<position<<" "<<name<<" is deleted."<<endl; } int main(){ int n; cout<<"The number of employees: "; cin>>n; employee *a = new employee[n]; for(int i = 0; i < n; i++){ int no; float time; string name, position; cout<<"Enter No: "; cin>>no; cout<<"Enter Name: "; cin>>name; cout<<"Enter Position: "; cin>>position; cout<<"Enter Time: "; cin>>time; a[i].setNo(no); a[i].setName(name); a[i].setPos(position); a[i].setTime(time); } delete[] a; return 0; }
说明
- 方案1保留了原有的
char*实现,但修复了所有问题,包括重载赋值运算符、正确创建数组、避免临时对象; - 方案2使用
std::string彻底消除了手动内存管理的麻烦,代码更简洁,适合C++初学者优先采用; - 两种方案中,所有员工对象的析构函数都会在
delete[] a;执行时被依次调用,符合“所有输入完成后再触发析构”的需求。
内容的提问来源于stack exchange,提问作者HowCanIBePro
相关产品推荐
相关产品推荐

