调用绑定成员函数

时间:2016-05-11 12:47:03

标签: c++11 gcc4.8

我有一个类根据传入的值调用函数。该函数为void,没有参数,存储在地图中(以及其他一些信息)。

程序编译并且函数golden_retriever按预期工作但是当labrador被调用时,程序SIGSEVs在gdb中具有以下信息(超出#5,它超出了测试类并进入实际代码):

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) where
#0  0x0000000000000000 in ?? ()
#1  0x000000000040dc71 in std::_Mem_fn<void (TestHandlerTwo::*)()>::operator()<, void>(TestHandlerTwo*) const (this=0x6416c0, __object=0x641400)
    at /usr/include/c++/4.8/functional:601
#2  0x000000000040d600 in std::_Bind<std::_Mem_fn<void (TestHandlerTwo::*)()> (TestHandlerTwo*)>::__call<void, , 0ul>(std::tuple<>&&, std::_Index_tuple<0ul>) (this=0x6416c0, 
    __args=<unknown type in /home/master/splint/SplintApp/test, CU 0x1eee, DIE 0x140c8>)
    at /usr/include/c++/4.8/functional:1296
#3  0x000000000040c90c in std::_Bind<std::_Mem_fn<void (TestHandlerTwo::*)()> (TestHandlerTwo*)>::operator()<, void>() (this=0x6416c0) at /usr/include/c++/4.8/functional:1355
#4  0x000000000040bcf3 in std::_Function_handler<void (), std::_Bind<std::_Mem_fn<void (TestHandlerTwo::*)()> (TestHandlerTwo*)> >::_M_invoke(std::_Any_data const&) (
    __functor=...) at /usr/include/c++/4.8/functional:2071
#5  0x000000000040ab5c in std::function<void ()>::operator()() const (this=0x641690)
    at /usr/include/c++/4.8/functional:2471

代码:

#include <iostream>
#include <map>
#include <memory>

struct command
{
    std::string cmdname;            // console friendly name
    std::function<void()> execute;  // function to call
};

class IHandler
{
public:
    virtual void parse(int value) = 0;
};

class BaseHandler : public IHandler
{
public:
    virtual auto getCommandMap() -> std::map<int, command> const = 0;

    void parse(int value) override
    {
        // this normally takes a stream of bytes and parses it but for this example we hardcode it.
        auto search = getCommandMap().find(value);
        if (search == getCommandMap().end())
        {
            return;
        }
        std::cout << "Function is " << (search->second.execute ? "callable" : "not callable") << std::endl;
        if (search->second.execute)
        {
            search->second.execute();
        }
        return;
    }
};

void golden_retriever()
{
    std::cout << "Chases cat" << std::endl;
}
class TestHandlerTwo : public BaseHandler
{
    std::map<int, command> commandMap = 
    {
        { 0x02, { "Handled", golden_retriever } },
        { 0x03, { "Test", std::bind(&TestHandlerTwo::labrador, this) } }
    };
public:
    void labrador()
    {
        std::cout << "Chases birds" << std::endl;
    }
    virtual auto getCommandMap() -> std::map<int, command> const override
    {
        return commandMap;
    }   
};

int main(int argc, char* argv[]) 
{
    auto testHandler = std::shared_ptr<IHandler>(new TestHandlerTwo());
    testHandler->parse(0x02);
    testHandler->parse(0x03);
    return 0;
}

其输出为:

(gdb) run
Starting program: /home/master/test/main 
Function is callable
Chases cat
Function is callable

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()

根据article以及StackOverflow上已经提出的问题,我对bind的使用似乎是正确的,所以我的代码有什么问题?

1 个答案:

答案 0 :(得分:1)

在容器(地图)销毁后,您正在访问迭代器。

在BaseHandler :: parse

void parse(int value) override
{
    // !!here, you constructs a map, and destruct it immediately
    // which invalidates the iterator
    // auto search = getCommandMap().find(value);
    // if (search == getCommandMap().end())
    // {
    //     return;
    // }
    // change to these three lines
    auto&& commandMap = getCommandMap();
    auto&& search = commandMap.find(value);
    if (search == commandMap.end()) return;

    std::cout << "Function is " << (search->second.execute ? "callable" : "not callable") << std::endl;
    if (search->second.execute)
    {
        search->second.execute();
    }
    return;
}

参见BaseHandler :: getCommandMap

// this always create a copy of original map, which considered
// temporary, destroys after execution of the statement if not
// being explicitly held.
virtual auto getCommandMap() -> std::map<int, command> const = 0;