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

C++基于范围的for循环致对象销毁(含重复销毁)问题排查

栈上Book对象析构异常的原因分析与解决

问题现象

  • 栈上创建的book1、book2等对象看似被立即销毁
  • 同一书名的析构函数被连续调用2-3次,调用菜单case3的showBooks函数时该现象重复出现

相关代码

#include <iostream>
#include <vector>

using namespace std;

class Book
{
    public:
        // Constructor Prototype
        Book(string author = "Auth", string title = "Title", string publisher = "Pub", float price = 1.99, int stock = 1);

        // Destructor Prototype
        ~Book();

        // Methods
        string get_title() const
        {
            return title;
        }

    private:
        string author;
        string title;
        string publisher;
        float price;
        int stock;
};

// Constructor definition
Book::Book(string author, string title, string publisher, float price, int stock)
    : author{author}, title{title}, publisher{publisher}, price{price}, stock{stock}
{
    cout << "Book: " << title << " was created." << endl;
}

// Destructor definition
Book::~Book()
{
    cout << "Book: " << title << " has been destroyed." << endl;
}

// Functions
void showMenu();
void getChoice();
void userChoice(int choice);
void showBooks(vector<Book> bookList);

// Global Vector
vector<Book> bookList;

int main()
{
    // Creating Book objects for testing purposes
    Book book1("Marcel Proust", "In Search of Lost Time", "Pub", 14.99, 5);
    Book book2("James Joyce", "Ulysses", "Pub", 25.99, 4);
    Book book3("Miguel de Cervantes", "Don Quixote", "Pub", 35.99, 3);
    Book book4("Gabriel Garcia Marquez", "One Hundred Years of Solitude", "Pub", 100.99, 2);
    Book book5("F. Scott Fitzgerald", "The Great Gatsby", "Pub", 49.99, 1);

    // Pushing book1-5 into the vector of Book objects
    bookList.push_back(book1);
    bookList.push_back(book2);
    bookList.push_back(book3);
    bookList.push_back(book4);
    bookList.push_back(book5);

    while(true)
    {
        showMenu();
        getChoice();
    }

    return 0;
}

void showMenu()
{
    cout << "\tMENU" << endl;
    cout << "1. Purchase Book" << endl;
    cout << "2. Search for Book" << endl;
    cout << "3. Show Books available" << endl;
    cout << "4. Add new Book" << endl;
    cout << "5. Edit details of Book" << endl;
    cout << "6. Exit" << endl;
}

void getChoice()
{
    int choice;
    cout << "Enter your choice (1-6): ";
    cin >> choice;
    userChoice(choice);
}

void userChoice(int choice)
{
    switch(choice)
    {
        case 1:
            cout << "Case 1 called." << endl;
            break;
        case 2:
            cout << "Case 2 called." << endl;
            break;
        case 3:
            cout << "Case 3 called." << endl;
            showBooks(bookList);
            break;
        case 4:
            cout << "Case 4 called." << endl;
            break;
        case 5:
            cout << "Case 5 called." << endl;
            break;
        case 6:
            cout << "Case 6 called." << endl;
            exit(0);
    }
}

void showBooks(vector<Book> bookList)
{
    for (Book bk : bookList)
    {
        cout << "Book: " << bk.get_title() << endl;
    }
}

运行输出示例

Book: In Search of Lost Time was created.
Book: Ulysses was created.
Book: Don Quixote was created.
Book: One Hundred Years of Solitude was created.       
Book: The Great Gatsby was created.
Book: In Search of Lost Time has been destroyed.       
Book: In Search of Lost Time has been destroyed.       
Book: Ulysses has been destroyed.
Book: In Search of Lost Time has been destroyed. 

原因分析

  1. vector扩容引发的拷贝对象销毁
    vector默认会在容量不足时自动扩容:每次push_back如果当前容量不够,会重新分配一块更大的内存,把现有元素拷贝到新内存,然后销毁旧内存里的元素。你看到的多次同一书名析构,就是旧内存里的拷贝对象被销毁导致的。而栈上的book1-book5并没有被立即销毁,它们的生命周期会持续到main函数结束。

  2. 传值调用导致的额外拷贝与销毁

    • showBooks函数的参数是vector<Book> bookList,这是传值传递,会把全局的bookList完整拷贝一份,函数执行完毕后,这个拷贝的vector会被销毁,里面的所有Book对象都会触发析构。
    • for循环中的Book bk : bookList也是值拷贝,每个循环都会创建一个bk的临时拷贝,循环结束后该临时对象被销毁,又会多一次析构调用。

解决方案

  1. 提前给vector预留容量
    在push_back之前调用bookList.reserve(5),让vector一开始就分配能容纳5个元素的空间,避免后续扩容时的拷贝与销毁:

    int main()
    {
        bookList.reserve(5); // 提前预留容量
        Book book1(...);
        // ... 其他book对象创建
        bookList.push_back(book1);
        // ... 其他push_back操作
    }
    
  2. 使用引用传递避免拷贝
    修改showBooks函数的参数为const引用,同时循环中也使用const引用遍历:

    void showBooks(const vector<Book>& bookList) // 引用传递
    {
        for (const Book& bk : bookList) // 引用遍历
        {
            cout << "Book: " << bk.get_title() << endl;
        }
    }
    
  3. 直接在vector中构造对象(可选)
    使用emplace_back代替push_back,直接在vector的内存空间中构造Book对象,不需要先在栈上创建再拷贝:

    int main()
    {
        bookList.reserve(5);
        bookList.emplace_back("Marcel Proust", "In Search of Lost Time", "Pub", 14.99, 5);
        bookList.emplace_back("James Joyce", "Ulysses", "Pub", 25.99, 4);
        // ... 其他书籍直接构造
        // 不再需要栈上的book1-book5对象
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:58:01