使用 std::shared_ptr 时内存新删除类型不匹配

Memory new-delete-type-mismatch when using std::shared_ptr

我正在尝试编写自定义数据结构:类似于永久转发自动释放列表的东西。 这是我的代码:

#include <queue>
#include <random>
#include <iostream>
#include <memory>
#include <cassert>
#include <fstream>

template<typename T>
struct RevListNode {
    T data;
    std::shared_ptr<RevListNode<T>> prev{};

    RevListNode(T data = {}, std::shared_ptr<RevListNode<T>> prev = {}) : data(data), prev(prev) {

    }
};

template<typename T>
struct RevList {

    std::shared_ptr<RevListNode<T>> last{};
    int sz = 0;

    [[nodiscard]] RevList Chain(T data) const {
        RevListNode<T> nw(data, last);
        RevList ans{std::make_shared<RevListNode<T>>(nw), sz + 1};
        assert(last == nullptr || last.use_count() > 1);
        return ans;
    }
};

struct State {
    RevList<int> taken_list{}; // more size is better
    int colors_eliminated = 0; // less is better

    bool operator<(const State &other) const {
        if (taken_list.sz > other.taken_list.sz) return true;
        if (taken_list.sz < other.taken_list.sz) return false;
        if (colors_eliminated < other.colors_eliminated) return true;
        if (colors_eliminated > other.colors_eliminated) return false;
        return false;
    }

    bool operator>(const State &other) const {
        return other < *this;
    }
};

using PrqState = std::priority_queue<State, std::vector<State>, std::greater<>>;

const int PURE_N = 9368;

int main(){
    std::vector<PrqState> q(PURE_N);
    int cnt = 0;
    std::ifstream in("log.txt");
    std::string s;
    q[0].emplace(State{});
    State cur = q[0].top();
    std::vector < std::array < int, 4 > > ops;
    while (in >> s){
        if (s == "pop"){
            int qind;
            in >> qind;
            ops.push_back({0, qind, 0, 0});
        } else {
            int qind, ind, el;
            in >> qind >> ind >> el;
            ops.push_back({1, qind, ind, el});
        }
    }
    for (int i = 0; i < ops.size();){
        //std::cerr << ops[i][0] << std::endl;
        assert(ops[i][0] == 0);
        int qind = ops[i][1];
        State cur = q[qind].top();
        q[qind].pop();
        int j = i + 1;
        for (; j < ops.size() && ops[j][0] == 1; ++j){
            //std::cerr << ops[j][0] << std::endl;
            qind = ops[j][1];
            int ind = ops[j][2];
            int el = ops[j][3];
            q[qind].emplace(State{cur.taken_list.Chain(ind), el});
        }
        i = j;
    }
}

我不知道某个部分什么时候会免费,所以我决定使用 std::shared_ptr(我从不在代码中取消引用它)。

日志link:https://drive.google.com/file/d/1vtJqNWSSHY-4saQdrSx7p0gC2S715GKd/view?usp=sharing

一些以前的版本,当我使用调试器时,它会向我显示类似于以下内容的内容: https://www.reddit.com/r/cpp_questions/comments/b03z69/copying_an_stdshared_ptr_causes_a_segmentation/

但我有日志(此代码模拟),它给我新的删除类型不匹配。 其实我不知道为什么会这样:

=================================================================
==13141==ERROR: AddressSanitizer: new-delete-type-mismatch on 0x604037f1bc10 in thread T0:
  object passed to delete has wrong type:
  size of the allocated type:   70368744177704 bytes;
  size of the deallocated type: 40 bytes.
    #0 0x7f0fcfd11777 in operator delete(void*, unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.6+0xb5777)
    #1 0x55a89e608cd3 in __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/11/ext/new_allocator.h:139
    #2 0x55a89e6088c9 in std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/11/bits/allocator.h:187
    #3 0x55a89e6088c9 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> > >::deallocate(std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >&, std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/11/bits/alloc_traits.h:492
    #4 0x55a89e608397 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> > >::~__allocated_ptr() /usr/include/c++/11/bits/allocated_ptr.h:73
    #5 0x55a89e608e92 in std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2>::_M_destroy() /usr/include/c++/11/bits/shared_ptr_base.h:538
    #6 0x55a89e6018ad in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/11/bits/shared_ptr_base.h:184
    #7 0x55a89e6008c7 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/11/bits/shared_ptr_base.h:702
    #8 0x55a89e6000b3 in std::__shared_ptr<RevListNode<int>, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/11/bits/shared_ptr_base.h:1149
    #9 0x55a89e6000cf in std::shared_ptr<RevListNode<int> >::~shared_ptr() /usr/include/c++/11/bits/shared_ptr.h:122
    #10 0x55a89e600331 in RevList<int>::~RevList() /home/arseny/Projects/RevList/main.cpp:19
    #11 0x55a89e60034d in State::~State() /home/arseny/Projects/RevList/main.cpp:32
    #12 0x55a89e606926 in void std::destroy_at<State>(State*) /usr/include/c++/11/bits/stl_construct.h:88
    #13 0x55a89e607fa9 in void std::_Destroy<State>(State*) /usr/include/c++/11/bits/stl_construct.h:138
    #14 0x55a89e607694 in void std::_Destroy_aux<false>::__destroy<State*>(State*, State*) /usr/include/c++/11/bits/stl_construct.h:152
    #15 0x55a89e606dd2 in void std::_Destroy<State*>(State*, State*) /usr/include/c++/11/bits/stl_construct.h:185
    #16 0x55a89e605758 in void std::_Destroy<State*, State>(State*, State*, std::allocator<State>&) /usr/include/c++/11/bits/alloc_traits.h:746
    #17 0x55a89e607e75 in std::vector<State, std::allocator<State> >::~vector() /usr/include/c++/11/bits/stl_vector.h:680
    #18 0x55a89e60741d in std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >::~priority_queue() /usr/include/c++/11/bits/stl_queue.h:456
    #19 0x55a89e607438 in void std::destroy_at<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > >(std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*) /usr/include/c++/11/bits/stl_construct.h:88
    #20 0x55a89e606c06 in void std::_Destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > >(std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*) /usr/include/c++/11/bits/stl_construct.h:138
    #21 0x55a89e60544f in void std::_Destroy_aux<false>::__destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*>(std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*, std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*) /usr/include/c++/11/bits/stl_construct.h:152
    #22 0x55a89e603460 in void std::_Destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*>(std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*, std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*) /usr/include/c++/11/bits/stl_construct.h:185
    #23 0x55a89e601be8 in void std::_Destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*, std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > >(std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*, std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*, std::allocator<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > >&) /usr/include/c++/11/bits/alloc_traits.h:746
    #24 0x55a89e6009e9 in std::vector<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >, std::allocator<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > > >::~vector() /usr/include/c++/11/bits/stl_vector.h:680
    #25 0x55a89e5ffa1d in main /home/arseny/Projects/RevList/main.cpp:88
    #26 0x7f0fceec9bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
    #27 0x55a89e5feb29 in _start (/home/arseny/Projects/RevList/main+0x2b29)

0x604037f1bc10 is located 0 bytes inside of 70368744177704-byte region [0x604037f1bc10,0xa04037f1bc38)
allocated by thread T0 here:
    #0 0x7f0fcfd10717 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.6+0xb4717)
    #1 0x55a89e608c97 in __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) /usr/include/c++/11/ext/new_allocator.h:121
    #2 0x55a89e6087f0 in std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long) /usr/include/c++/11/bits/allocator.h:173
    #3 0x55a89e6087f0 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) /usr/include/c++/11/bits/alloc_traits.h:460
    #4 0x55a89e6082fd in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2> >&) /usr/include/c++/11/bits/allocated_ptr.h:97
    #5 0x55a89e607a91 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<RevListNode<int>, std::allocator<RevListNode<int> >, RevListNode<int>&>(RevListNode<int>*&, std::_Sp_alloc_shared_tag<std::allocator<RevListNode<int> > >, RevListNode<int>&) /usr/include/c++/11/bits/shared_ptr_base.h:648
    #6 0x55a89e6072db in std::__shared_ptr<RevListNode<int>, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<RevListNode<int> >, RevListNode<int>&>(std::_Sp_alloc_shared_tag<std::allocator<RevListNode<int> > >, RevListNode<int>&) /usr/include/c++/11/bits/shared_ptr_base.h:1337
    #7 0x55a89e606a08 in std::shared_ptr<RevListNode<int> >::shared_ptr<std::allocator<RevListNode<int> >, RevListNode<int>&>(std::_Sp_alloc_shared_tag<std::allocator<RevListNode<int> > >, RevListNode<int>&) /usr/include/c++/11/bits/shared_ptr.h:409
    #8 0x55a89e605068 in std::shared_ptr<RevListNode<int> > std::allocate_shared<RevListNode<int>, std::allocator<RevListNode<int> >, RevListNode<int>&>(std::allocator<RevListNode<int> > const&, RevListNode<int>&) /usr/include/c++/11/bits/shared_ptr.h:861
    #9 0x55a89e602eb8 in std::shared_ptr<RevListNode<int> > std::make_shared<RevListNode<int>, RevListNode<int>&>(RevListNode<int>&) /usr/include/c++/11/bits/shared_ptr.h:877
    #10 0x55a89e60114f in RevList<int>::Chain(int) const /home/arseny/Projects/RevList/main.cpp:26
    #11 0x55a89e5ff85f in main /home/arseny/Projects/RevList/main.cpp:84
    #12 0x7f0fceec9bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)

SUMMARY: AddressSanitizer: new-delete-type-mismatch (/usr/lib/x86_64-linux-gnu/libasan.so.6+0xb5777) in operator delete(void*, unsigned long)
==13141==HINT: if you don't care about these errors you may set ASAN_OPTIONS=new_delete_type_mismatch=0
==13141==ABORTING

我使用这个 bash 脚本编译并 运行 它(如果我 运行 这个脚本大约 5 次我至少得到一次 new-delete-type-mismatch):

#!/bin/bash
g++ -fsanitize=address -g3 -fno-omit-frame-pointer -O0 -ggdb --std=c++20 -o main main.cpp;
time ./main;

当我 运行 没有消毒剂时核心转储回溯:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055af3a2eae23 in __gnu_cxx::__exchange_and_add_single (__val=-1, __mem=0x55aff507be58) at /usr/include/c++/11/ext/atomicity.h:84
84          _Atomic_word __result = *__mem;
(gdb) bt
#0  0x000055af3a2eae23 in __gnu_cxx::__exchange_and_add_single (__val=-1, __mem=0x55aff507be58) at /usr/include/c++/11/ext/atomicity.h:84
#1  __gnu_cxx::__exchange_and_add_dispatch (__val=-1, __mem=0x55aff507be58) at /usr/include/c++/11/ext/atomicity.h:99
#2  std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x55aff507be50) at /usr/include/c++/11/bits/shared_ptr_base.h:165
#3  0x000055af3a2ea6c1 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x55afb51bbc00, __in_chrg=<optimized out>)
    at /usr/include/c++/11/bits/shared_ptr_base.h:702
#4  0x000055af3a2ea374 in std::__shared_ptr<RevListNode<int>, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x55afb51bbbf8, __in_chrg=<optimized out>)
    at /usr/include/c++/11/bits/shared_ptr_base.h:1149
#5  0x000055af3a2ea390 in std::shared_ptr<RevListNode<int> >::~shared_ptr (this=0x55afb51bbbf8, __in_chrg=<optimized out>) at /usr/include/c++/11/bits/shared_ptr.h:122
#6  0x000055af3a2eaaea in RevListNode<int>::~RevListNode (this=0x55afb51bbbf0, __in_chrg=<optimized out>) at main.cpp:9
#7  0x000055af3a2ee82d in std::destroy_at<RevListNode<int> > (__location=0x55afb51bbbf0) at /usr/include/c++/11/bits/stl_construct.h:88
#8  0x000055af3a2ee804 in std::allocator_traits<std::allocator<RevListNode<int> > >::destroy<RevListNode<int> > (__a=..., __p=0x55afb51bbbf0)
    at /usr/include/c++/11/bits/alloc_traits.h:533
#9  0x000055af3a2ee6d7 in std::_Sp_counted_ptr_inplace<RevListNode<int>, std::allocator<RevListNode<int> >, (__gnu_cxx::_Lock_policy)2>::_M_dispose (this=0x55afb51bbbe0)
    at /usr/include/c++/11/bits/shared_ptr_base.h:528
#10 0x000055af3a2eae7f in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x55afb51bbbe0) at /usr/include/c++/11/bits/shared_ptr_base.h:168
#11 0x000055af3a2ea6c1 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x55afb5239a88, __in_chrg=<optimized out>)
    at /usr/include/c++/11/bits/shared_ptr_base.h:702
#12 0x000055af3a2ea374 in std::__shared_ptr<RevListNode<int>, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x55afb5239a80, __in_chrg=<optimized out>)
    at /usr/include/c++/11/bits/shared_ptr_base.h:1149
#13 0x000055af3a2ea390 in std::shared_ptr<RevListNode<int> >::~shared_ptr (this=0x55afb5239a80, __in_chrg=<optimized out>) at /usr/include/c++/11/bits/shared_ptr.h:122
#14 0x000055af3a2ea44a in RevList<int>::~RevList (this=0x55afb5239a80, __in_chrg=<optimized out>) at main.cpp:19
#15 0x000055af3a2ea466 in State::~State (this=0x55afb5239a80, __in_chrg=<optimized out>) at main.cpp:32
#16 0x000055af3a2ed161 in std::destroy_at<State> (__location=0x55afb5239a80) at /usr/include/c++/11/bits/stl_construct.h:88
#17 0x000055af3a2ede2e in std::_Destroy<State> (__pointer=0x55afb5239a80) at /usr/include/c++/11/bits/stl_construct.h:138
#18 0x000055af3a2ed907 in std::_Destroy_aux<false>::__destroy<State*> (__first=0x55afb5239a80, __last=0x55afb523ff20) at /usr/include/c++/11/bits/stl_construct.h:152
#19 0x000055af3a2ed441 in std::_Destroy<State*> (__first=0x55afb51c2120, __last=0x55afb523ff20) at /usr/include/c++/11/bits/stl_construct.h:185
#20 0x000055af3a2ecac1 in std::_Destroy<State*, State> (__first=0x55afb51c2120, __last=0x55afb523ff20) at /usr/include/c++/11/bits/alloc_traits.h:746
#21 0x000055af3a2edd05 in std::vector<State, std::allocator<State> >::~vector (this=0x7fc03971e830, __in_chrg=<optimized out>) at /usr/include/c++/11/bits/stl_vector.h:680
#22 0x000055af3a2ed7d8 in std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >::~priority_queue (this=0x7fc03971e830, __in_chrg=<optimized out>)
    at /usr/include/c++/11/bits/stl_queue.h:456
#23 0x000055af3a2ed7f3 in std::destroy_at<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > > (__location=0x7fc03971e830)
    at /usr/include/c++/11/bits/stl_construct.h:88
#24 0x000055af3a2ed304 in std::_Destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > > (__pointer=0x7fc03971e830)
    at /usr/include/c++/11/bits/stl_construct.h:138
#25 0x000055af3a2ec926 in std::_Destroy_aux<false>::__destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*> (__first=0x7fc03971e830, 
--Type <RET> for more, q to quit, c to continue without paging--
    t=0x7fc03975d310) at /usr/include/c++/11/bits/stl_construct.h:152
#26 0x000055af3a2ebb79 in std::_Destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*> (__first=0x7fc039714010, __last=0x7fc03975d310)
    at /usr/include/c++/11/bits/stl_construct.h:185
#27 0x000055af3a2eb0d9 in std::_Destroy<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >*, std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > > (__first=0x7fc039714010, __last=0x7fc03975d310) at /usr/include/c++/11/bits/alloc_traits.h:746
#28 0x000055af3a2ea797 in std::vector<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> >, std::allocator<std::priority_queue<State, std::vector<State, std::allocator<State> >, std::greater<void> > > >::~vector (this=0x7ffd49efdb40, __in_chrg=<optimized out>) at /usr/include/c++/11/bits/stl_vector.h:680
#29 0x000055af3a2ea04e in main () at main.cpp:88

我的 g++ 版本:

g++ --version
g++ (Ubuntu 11.1.0-1ubuntu1~18.04.1) 11.1.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

当使用 std::priority_queue::top() 时,它不检查是否 size() > 0,所以 std::priority_queue::top() 使用释放内存来使用 shared_ptr 方法,所以导致不好的事情