调用绑定成员函数时出现分段错误
Segmentation fault when calling bound member function
我有一个 class 根据传入的值调用一个函数。该函数是空的,没有参数,并存储在映射中(连同一些其他信息)。
程序编译并且函数 golden_retriever
按预期工作但是当 labrador
被调用时程序 SIGSEVs 在 gdb 中具有以下信息(超过 #5 它超出了测试 class 并进入实际代码):
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 和 Whosebug 上已经提出的问题,我对 bind
的用法似乎是正确的,那么我的代码有什么问题?
您正在容器(地图)销毁后访问迭代器。
在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;
我有一个 class 根据传入的值调用一个函数。该函数是空的,没有参数,并存储在映射中(连同一些其他信息)。
程序编译并且函数 golden_retriever
按预期工作但是当 labrador
被调用时程序 SIGSEVs 在 gdb 中具有以下信息(超过 #5 它超出了测试 class 并进入实际代码):
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 和 Whosebug 上已经提出的问题,我对 bind
的用法似乎是正确的,那么我的代码有什么问题?
您正在容器(地图)销毁后访问迭代器。
在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;