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

需为多态基类添加虚析构?如何实现内存释放与析构继承?

如何在你的电话客户链表程序中正确释放内存(结合析构继承原理)

嘿,我仔细看了你的电话客户账单管理程序,明白你现在纠结内存释放和析构继承的问题——别慌,我结合你的代码一步步给你讲清楚怎么做,顺便拆解析构继承的工作逻辑。

第一步:给基类添加虚析构函数

你用了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; 
}

析构继承的工作原理拆解

当你有类继承关系时,析构函数的调用顺序是从派生类到基类,而虚析构是让这个过程正确触发的关键:

  1. 当你通过BaseTypeOfContract*指针删除一个MobiNET对象时,因为基类析构是虚的,编译器会找到实际指向的MobiNET类型,先调用MobiNET的默认析构
  2. 接着调用它的基类SMSBaseType的默认析构
  3. 最后调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:32:18