需为多态基类添加虚析构?如何实现内存释放与析构继承?
如何在你的电话客户链表程序中正确释放内存(结合析构继承原理)
嘿,我仔细看了你的电话客户账单管理程序,明白你现在纠结内存释放和析构继承的问题——别慌,我结合你的代码一步步给你讲清楚怎么做,顺便拆解析构继承的工作逻辑。
第一步:给基类添加虚析构函数
你用了BaseTypeOfContract*指针指向派生类对象(比如MobiNET、SMSMax),如果基类没有虚析构函数,当你通过基类指针删除派生类对象时,只会调用基类的析构,派生类的资源(哪怕现在没有,以后加了就会出问题)不会被释放,直接导致内存泄漏。
修改你的BaseTypeOfContract类,加上虚析构:
class BaseTypeOfContract { private: int minutePrice; int SMSPrice; public: // 核心:添加虚析构函数,确保派生类析构被正确调用 virtual ~BaseTypeOfContract() = default; void setminutePrice(int x) { minutePrice = x; } void setSMSPrice(int x) { SMSPrice = x; } virtual int calculateBill(int talkedMinutes, int sentSMS) = 0; int getminutePrice() const { return minutePrice; } int getSMSPrice() const { return SMSPrice; } };
第二步:让Client类负责释放自己持有的资源
Client类里的typeOfContract是动态分配的(在preSetPlan里用new创建),所以Client的析构函数必须手动释放这个指针。同时注意不要在Client析构里直接删除next——链表节点是链式连接的,递归删除可能导致栈溢出,我们后面用专门的函数遍历释放整个链表。
修改Client类:
class Client { public: std::string name; std::string phoneNumber; BaseTypeOfContract* typeOfContract; int talkedMinutes; int sentSMS; Client *next; public: // 初始化指针为nullptr,避免野指针 Client() : typeOfContract(nullptr), next(nullptr) {} Client(std::string n, std::string p, int bp, int ks) : name(n), phoneNumber(p), talkedMinutes(bp), sentSMS(ks), typeOfContract(nullptr), next(nullptr) {} // 析构函数:释放自己持有的合约对象 ~Client() { delete typeOfContract; typeOfContract = nullptr; // 可选,防止野指针 } // 其他成员函数保持不变... };
第三步:写一个函数释放整个链表
遍历链表,逐个删除每个Client节点,这样每个节点的析构函数会自动调用,进而释放对应的typeOfContract对象:
void deleteClientList(Client* &first) { while (first != nullptr) { Client* temp = first; first = first->next; delete temp; // 触发Client析构,自动释放typeOfContract } }
然后在main函数里调用这个函数,完成内存释放:
int main() { Client* first = file_read_in(); // 用current遍历输出,不修改first,方便后续释放 Client* current = first; while(current != nullptr) { std::cout << current->getname() << " " << current->getphoneNumber() << " " << current->getBill() << std::endl; current = current->next; } // 释放整个链表 deleteClientList(first); return 0; }
析构继承的工作原理拆解
当你有类继承关系时,析构函数的调用顺序是从派生类到基类,而虚析构是让这个过程正确触发的关键:
- 当你通过
BaseTypeOfContract*指针删除一个MobiNET对象时,因为基类析构是虚的,编译器会找到实际指向的MobiNET类型,先调用MobiNET的默认析构 - 接着调用它的基类
SMSBaseType的默认析构 - 最后调用
BaseTypeOfContract的虚析构
如果基类没有虚析构,编译器只会调用BaseTypeOfContract的析构,MobiNET和SMSBaseType的析构完全不会执行——哪怕现在没有额外资源,以后只要给派生类加动态分配的变量,就会直接内存泄漏。
额外小优化:提升文件读取效率
你的file_read_in函数每次读一个客户就打开一次used.txt,效率很低。可以先把used.txt的内容读到一个map里,之后直接通过电话号码查找,只需要打开一次文件:
#include <map> Client* file_read_in() { // 先把使用记录读到map,key是手机号,value是通话时长+短信数量 std::map<std::string, std::pair<int, int>> usageMap; std::ifstream ClientTalkedSent("used.txt"); if (ClientTalkedSent.is_open()) { std::string phoneNumber; int talkedMinutes, sentSMS; while (ClientTalkedSent >> phoneNumber >> talkedMinutes >> sentSMS) { usageMap[phoneNumber] = {talkedMinutes, sentSMS}; } ClientTalkedSent.close(); } std::ifstream ClientData("clients.txt"); Client *first = nullptr; Client *last = nullptr; // 跟踪链表末尾,不用每次遍历找最后一个节点 if (ClientData.is_open()) { std::string name, phoneNumber, typeOfContract; while (ClientData >> name >> phoneNumber >> typeOfContract) { auto it = usageMap.find(phoneNumber); if (it != usageMap.end()) { int talkedMinutes = it->second.first; int sentSMS = it->second.second; Client* tmp = new Client(name, phoneNumber, talkedMinutes, sentSMS); tmp->preSetPlan(typeOfContract); tmp->next = nullptr; if (first == nullptr) { first = tmp; last = tmp; } else { last->next = tmp; last = tmp; } } } ClientData.close(); } return first; }
内容的提问来源于stack exchange,提问作者Melvin
相关产品推荐
相关产品推荐

