c++ - 删除移动到容器的对象时的SIGABRT

标签 c++ c++11

我有一个界面:

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>> 类型的成员

它还创建接口(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

逐步调试显示,当Pool被摧毁m_is_borrowed为 false,因此不会创建新的 unique_ptr 实例。是什么导致了这个问题?

最佳答案

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

实际上您还没有从上面的池中删除d 指针。在调用get()前后检查m_objectssize(),你会发现大小和之前完全一样之后。您将在 front() 处留下一个空的 unique_ptr

考虑这样的事情:

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

或者更有效地,让它从 back() 中选择一个,因为这样 vector 就不必移动它的数据了:

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_ptr 替换为具有一些日志记录的 smartsmart 的每个实例还有一个可以跟踪的 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)

关于c++ - 删除移动到容器的对象时的SIGABRT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54708544/

相关文章:

c++ - 克服 Visual Studio 2013 中的 decltype 问题

c++ - push_back 一个字符从一个字符串到另一个字符串

c++ - 如何以下列方式格式化 double ?

c++ - 如何正确解释数字(十六进制、十进制、十进制)

c++ - C++ (gcc) 中是否有等效的 TryParse?

c++ - 对模板的`vtable 的 undefined reference

c++ - 代码不泄露时编译执行;在代码中使用省略号

c++ - C++中位运算符的定义是什么?

c++ - 用 lambda 初始化 std::unique_ptr 的 std::vector

c++ - 消除函数参数的复制