首页 > 解决方案 > 删除移动到容器的对象时的 SIGABRT

问题描述

我有一个界面:

struct Interface{
  Interface(const Interface &) = delete;
  auto operator = (const Interface &) -> Interface & = delete;
  virtual ~Interface() = 0;
};

我有一个实现:

struct Impl : public Interface {
  Impl(const Impl &) = delete;
  auto operator = (const Impl &) -> Impl & = delete;
  bool m_is_borrowed{false};
  std::weak_ptr<Pool> m_pool;

  ~Impl() {
    if (m_is_borrowed) {
      if (auto pool = m_pool.lock()) {
        m_is_borrowed = false;
        auto conn = std::unique_ptr<Interface>(this);
        pool->m_objects.push_back(std::move(conn));
      }
    }
  }
};

正如代码所示Pool,有一个类型的成员std::vector<std::unique_ptr<Interface>>

它还创建接口实例:

auto impl = std::unique_ptr<Interface>(new Implementation());
m_objects.push_back(std::move(impl));

并给用户一个Pool

auto get() -> std::unique_ptr<Interface> {
  return std::move(m_objects.front());
}

所以我想做的是,当一个实例Implementation被删除时,检查它是否被借用Pool,如果是这样,把它送回池中。如果它池中,则让它正常删除。

将实例发送回池是可行的,但是当我尝试在删除对象时删除对象时Pool,出现以下错误:

integration(46154,0x1003855c0) malloc: *** error for object 0x101900000: pointer being freed was not allocated
integration(46154,0x1003855c0) malloc: *** set a breakpoint in malloc_error_break to debug

使用以下堆栈跟踪:

* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff6418d23e libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff64243c1c libsystem_pthread.dylib`pthread_kill + 285
    frame #2: 0x00007fff640f61c9 libsystem_c.dylib`abort + 127
    frame #3: 0x00007fff642056e2 libsystem_malloc.dylib`malloc_vreport + 545
    frame #4: 0x00007fff642054a3 libsystem_malloc.dylib`malloc_report + 152
    frame #5: 0x000000010000fa82 integration`Implementation::~Implementation(this=0x0000000101900000) at Implementation.cpp:14
    frame #6: 0x000000010000a449 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::default_delete<Interface>::operator(this=0x0000000101800c58, __ptr=0x0000000101900000)(Interface*) const at memory:2285
    frame #7: 0x000000010000a421 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >::reset(this=0x0000000101800c58, __p=0x0000000000000000) at memory:2598
    frame #8: 0x000000010000a3b0 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >::~unique_ptr(this=0x0000000101800c58) at memory:2552
    frame #9: 0x000000010000a3b0 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >::~unique_ptr(this=0x0000000101800c58) at memory:2552
    frame #10: 0x000000010000a3b0 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > >::destroy(this=0x0000000101800a58, __p=0x0000000101800c58) at memory:1860
    frame #11: 0x000000010000a358 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] void std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::__destroy<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > >(__a=0x0000000101800a58, __p=0x0000000101800c58) at memory:1727
    frame #12: 0x000000010000a33c integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] void std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::destroy<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > >(__a=0x0000000101800a58, __p=0x0000000101800c58) at memory:1595
    frame #13: 0x000000010000a320 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::__destruct_at_end(this=0x0000000101800a48, __new_last=0x0000000101800c50) at vector:413
    frame #14: 0x000000010000a297 integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base() [inlined] std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::clear(this=0x0000000101800a48) at vector:356
    frame #15: 0x000000010000a27f integration`std::__1::__vector_base<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~__vector_base(this=0x0000000101800a48) at vector:441
    frame #16: 0x000000010000a235 integration`std::__1::vector<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~vector(this=0x0000000101800a48 size=2) at iterator:1425
    frame #17: 0x0000000100002395 integration`std::__1::vector<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> >, std::__1::allocator<std::__1::unique_ptr<Interface, std::__1::default_delete<Interface> > > >::~vector(this=0x0000000101800a48 size=2) at iterator:1425
    frame #18: 0x0000000100002478 integration`Pool::~Pool(this=0x00000001018009d8) at Pool.cpp:17
    frame #19: 0x0000000100002565 integration`Pool::~Pool(this=0x00000001018009d8) at Pool.cpp:15
    frame #20: 0x0000000100167029 integration`std::__1::__shared_ptr_emplace<Pool, std::__1::allocator<Pool> >::__on_zero_shared(this=0x00000001018009c0) at memory:3656
    frame #21: 0x000000010000cc01 integration`std::__1::shared_ptr<Pool>::~shared_ptr() [inlined] std::__1::__shared_count::__release_shared(this=0x00000001018009c0) at memory:3490
    frame #22: 0x000000010000cbb7 integration`std::__1::shared_ptr<Pool>::~shared_ptr() [inlined] std::__1::__shared_weak_count::__release_shared(this=0x00000001018009c0) at memory:3532
    frame #23: 0x000000010000cbb7 integration`std::__1::shared_ptr<Pool>::~shared_ptr(this=0x00007ffeefbfe1a0) at memory:4468
    frame #24: 0x0000000100002da5 integration`std::__1::shared_ptr<Pool>::~shared_ptr(this=0x00007ffeefbfe1a0) at memory:4466
    frame #25: 0x0000000100165d1f integration`____C_A_T_C_H____T_E_S_T____0() at Pool.cpp:30
    frame #26: 0x00000001000516e3 integration`Catch::TestInvokerAsFunction::invoke(this=0x0000000100702b90) const at catch.hpp:11605
    frame #27: 0x000000010003e277 integration`Catch::TestCase::invoke(this=0x0000000101800760) const at catch.hpp:11506
    frame #28: 0x000000010003e16d integration`Catch::RunContext::invokeActiveTestCase(this=0x00007ffeefbff308) at catch.hpp:10365
    frame #29: 0x0000000100039abb integration`Catch::RunContext::runCurrentTest(this=0x00007ffeefbff308, redirectedCout="", redirectedCerr="") at catch.hpp:10339
    frame #30: 0x0000000100037387 integration`Catch::RunContext::runTest(this=0x00007ffeefbff308, testCase=0x0000000101800760) at catch.hpp:10115
    frame #31: 0x0000000100042c39 integration`Catch::(anonymous namespace)::runTests(config=std::__1::shared_ptr<Catch::Config>::element_type @ 0x0000000101800228 strong=4 weak=1) at catch.hpp:10667
    frame #32: 0x00000001000417e6 integration`Catch::Session::runInternal(this=0x00007ffeefbff780) at catch.hpp:10862
    frame #33: 0x00000001000414c5 integration`Catch::Session::run(this=0x00007ffeefbff780) at catch.hpp:10819
    frame #34: 0x000000010007ef8a integration`int Catch::Session::run<char>(this=0x00007ffeefbff780, argc=2, argv=0x00007ffeefbff8f8) at catch.hpp:10565
    frame #35: 0x000000010007eed2 integration`main(argc=2, argv=0x00007ffeefbff8f8) at catch.hpp:14318
    frame #36: 0x00007fff6404ded9 libdyld.dylib`start + 1
    frame #37: 0x00007fff6404ded9 libdyld.dylib`start + 1

逐步调试显示 when Poolis m_is_borroweddestroy 为假,因此不会创建新的 unique_ptr 实例。什么可能导致此问题?

标签: c++c++11

解决方案


auto get() -> std::unique_ptr<Interface> {
    return std::move(m_objects.front());
}

您实际上还没有erased 上面池中的指针。检查调用之前和之后size()的大小,你会发现之前和之后的大小完全一样。你在处留空。m_objectsget()unique_ptrfront()

考虑类似的事情:

auto get() -> std::unique_ptr<Interface> {
    auto rv = std::move(m_objects.front());
    m_objects.erase(m_objects.begin());
    return rv;
}

或者更有效地,让它从back()向量中选择一个,然后就不必移动它的数据:

auto get() -> std::unique_ptr<Interface> {
    auto rv = std::move(m_objects.back());
    m_objects.pop_back();
    return rv;
}

问题二:

~Impl() {
    if (m_is_borrowed) {
        if (auto pool = m_pool.lock()) {
            m_is_borrowed = false;
            auto conn = std::unique_ptr<Interface>(this);
            pool->m_objects.push_back(std::move(conn));
        }
    }
}

在这里,您创建一个unique_ptr并赋予其所有权this。返回时~Impl(),指向的内存this将(通常)为freed。现在unique_ptr拥有一个无效的指针,并且delete在对该指针执行操作时,任何事情都可能发生。

这是一个例子来说明它。我已经替换unique_ptrsmart有一些日志记录。每个实例smart也有一个id可以跟踪的。

#include <iostream>
#include <vector>

template<typename T>
struct smart {
    static int instance;
    int id;
    T* ptr;

    smart() : id(++instance), ptr(nullptr) {}
    smart(T* p) : id(++instance), ptr(p) {
        std::cout << "smart("<<id<<") ctor got ptr: " << std::boolalpha << (ptr!=nullptr) << "\n";
    }
    smart(const smart&) = delete;
    smart(smart&& o) : id(++instance), ptr(std::exchange(o.ptr, nullptr)) {
        std::cout << "smart("<<id<<") move ctor from "<<o.id<<"\n";
    }
    smart& operator=(const smart&) = delete;
    smart& operator=(smart&& o) {
        delete ptr;
        ptr = std::exchange(o.ptr, nullptr);
        std::cout << "smart("<<id<<") move assign from "<<o.id<<"\n";
    }
    ~smart() {
        std::cout << "~smart("<<id<<") real delete: " << std::boolalpha << (ptr!=nullptr) << "\n";
        delete ptr;
    }
};
template<typename T>
int smart<T>::instance = 0;

struct try_me;

std::vector<smart<try_me>> pool;

struct try_me {
    try_me() { std::cout << "try_me\n"; }
    ~try_me() {
        std::cout << "~try_me -- start --\n";
        auto tmp = smart<try_me>(this);
        pool.push_back(std::move(tmp));
        std::cout << "~try_me -- end --\n";
    }
};

int main() {
    pool.emplace_back(new try_me);
    std::cout << "--------------\n";
    pool.pop_back();
    std::cout << "--------------\n";
}

可能的输出:

try_me
smart(1) ctor got ptr: true
--------------
~smart(1) real delete: true
~try_me -- start --
smart(2) ctor got ptr: true
smart(3) move ctor from 2
~try_me -- end --
~smart(2) real delete: false
--------------
~smart(3) real delete: true
~try_me -- start --
smart(4) ctor got ptr: true
smart(5) move ctor from 4
smart(6) move ctor from 3
~smart(3) real delete: false
~try_me -- end --
~smart(4) real delete: false
free(): double free detected in tcache 2
Aborted (core dumped)

推荐阅读