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

C++使用shared_ptr实现约瑟夫环时的内存泄漏问题求助

约瑟夫环问题中shared_ptr的内存泄漏排查与修复

我在解决一个城市区域停水排序问题(类似约瑟夫环),要求餐厅所在区域最后停水。用shared_ptr实现的代码功能正常,示例输出正确,但LeakSanitizer检测到2处共80字节的内存泄漏,求泄漏原因及修复方法。

原代码

#include <iostream>
#include <stdexcept>
#include <vector>
#include <memory>

using namespace std;

struct District {
    int district_number;
    shared_ptr<District> next;
};

vector<int> Reduction(int N, int M) {
    vector<int> reduction_order;

    auto start = make_shared<District>();
    start->district_number = 1;
    auto previous = start;

    for (int i = 2; i <= N; i++) {
        auto new_district = make_shared<District>();
        new_district->district_number = i;
        previous->next = new_district;
        previous = previous->next;
    }
    previous->next = start;

    int counter = 0;
    auto current = previous->next;

    while (current != current->next) {
        if (counter % M == 0) {
            reduction_order.push_back(current->district_number);
            auto temp = current->next;
            previous->next = temp;
            current = nullptr;
            current = previous->next;
        } else {
            previous = current;
            current = current->next;
        }
        
        counter++;
    }
    reduction_order.push_back(current->district_number);
    current = nullptr;

    return reduction_order;
}

int StepSelection(int N, int K) {
    if (N <= 0 || K <= 0 || K > N)
        throw domain_error("The number of districts and the ordinal number of the district are positive integers and the ordinal number of the district cannot be greater than the number of districts.");
    if (N != 1 && K == 1)
        return 0;
    int M(1);

    vector<int> reduction_order(Reduction(N, M));

    while (reduction_order[reduction_order.size() - 1] != K) {
        M++;
        reduction_order = Reduction(N, M);
        if (M >= 5 * N) {
            M = 0;
            break;
        }
    }

    return M;
}

int main() {
    try {
        int N, K;
        cout << "Enter the number of districts in the city: ";
        cin >> N;
        cout << "Enter the ordinal number of the district where the restaurant is located: ";
        cin >> K;
        cout << "Required step: " << StepSelection(N, K);
    }
    catch (domain_error& d) {
        cout << d.what();
    }

    return 0;
}

示例输出

Enter the number of districts in the city: 10
Enter the ordinal number of the district where the restaurant is located: 4
Required step: 2

LeakSanitizer检测信息

==1453486==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f2ecb337587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104
#1 0x55c4ecd58b96 in __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) /usr/include/c++/9/ext/new_allocator.h:114
#2 0x55c4ecd587ac in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) /usr/include/c++/9/bits/alloc_traits.h:443
#3 0x55c4ecd58179 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> >&) /usr/include/c++/9/bits/allocated_ptr.h:97
#4 0x55c4ecd57ade in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<District, std::allocator<District> >(District*&, std::_Sp_alloc_shared_tag<std::allocator<District> >) /usr/include/c++/9/bits/shared_ptr_base.h:677
#5 0x55c4ecd5718c in std::__shared_ptr<District, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<District> >(std::_Sp_alloc_shared_tag<std::allocator<District> >) /usr/include/c++/9/bits/shared_ptr_base.h:1344
#6 0x55c4ecd56463 in std::shared_ptr<District>::shared_ptr<std::allocator<District> >(std::_Sp_alloc_shared_tag<std::allocator<District> >) /usr/include/c++/9/bits/shared_ptr.h:359
#7 0x55c4ecd5543f in std::shared_ptr<District> std::allocate_shared<District, std::allocator<District> >(std::allocator<District> const&) /usr/include/c++/9/bits/shared_ptr.h:702
#8 0x55c4ecd54c8f in std::shared_ptr<District> std::make_shared<District>() /usr/include/c++/9/bits/shared_ptr.h:718
#9 0x55c4ecd5381e in Reduction(int, int) main.cpp:28
#10 0x55c4ecd53fc0 in StepSelection(int, int) main.cpp:70
#11 0x55c4ecd54287 in _main() main.cpp:87
#12 0x55c4ecd54415 in main main.cpp:101
#13 0x7f2ecad0e082 in __libc_start_main ../csu/libc-start.c:308

Direct leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f2ecb337587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104
#1 0x55c4ecd58b96 in __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) /usr/include/c++/9/ext/new_allocator.h:114
#2 0x55c4ecd587ac in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) /usr/include/c++/9/bits/alloc_traits.h:443
#3 0x55c4ecd58179 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<District, std::allocator<District>, (__gnu_cxx::_Lock_policy)2> >&) /usr/include/c++/9/bits/allocated_ptr.h:97
#4 0x55c4ecd57ade in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<District, std::allocator<District> >(District*&, std::_Sp_alloc_shared_tag<std::allocator<District> >) /usr/include/c++/9/bits/shared_ptr_base.h:677
#5 0x55c4ecd5718c in std::__shared_ptr<District, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<District> >(std::_Sp_alloc_shared_tag<std::allocator<District> >) /usr/include/c++/9/bits/shared_ptr_base.h:1344
#6 0x55c4ecd56463 in std::shared_ptr<District>::shared_ptr<std::allocator<District> >(std::_Sp_alloc_shared_tag<std::allocator<District> >) /usr/include/c++/9/bits/shared_ptr.h:359
#7 0x55c4ecd5543f in std::shared_ptr<District> std::allocate_shared<District, std::allocator<District> >(std::allocator<District> const&) /usr/include/c++/9/bits/shared_ptr.h:702
#8 0x55c4ecd54c8f in std::shared_ptr<District> std::make_shared<District>() /usr/include/c++/9/bits/shared_ptr.h:718
#9 0x55c4ecd5381e in Reduction(int, int) main.cpp:28
#10 0x55c4ecd53eb2 in StepSelection(int, int) main.cpp:66
#11 0x55c4ecd54287 in _main() main.cpp:87
#12 0x55c4ecd54415 in main main.cpp:101
#13 0x7f2ecad0e082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: 80 byte(s) leaked in 2 allocation(s).

泄漏原因

  • 循环引用导致引用计数无法归零:环形链表中,最后剩余的单个District节点的next指针是指向自身的shared_ptr,形成循环引用。shared_ptr的引用计数会保持为1,因为节点自身的next还持有对自己的引用,导致内存无法被自动回收。每次调用Reduction函数都会产生一个这样的循环引用节点,多次调用就会累积泄漏。
  • 代码中仅设置current = nullptr,但未打破环形引用,shared_ptr的引用计数无法降到0,内存无法释放。

修复方法

只需在Reduction函数结束前,手动打破最后剩余节点的循环引用,让shared_ptr的引用计数可以正常归零:

修改Reduction函数的最后部分:

reduction_order.push_back(current->district_number);
    current->next.reset(); // 打破循环引用
    current = nullptr;

    return reduction_order;

修改后,最后剩余的节点不再持有指向自己的shared_ptr,当current被置为nullptr后,该节点的引用计数变为0,内存会被正确释放,LeakSanitizer将不再检测到泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:37:02