[英]C++ stuck in infinite loop of equal operator assignment
我发现自己陷入了一种地狱般的噩梦,我试图使用等号运算符重新分配一个 object,它通过另一个内部 object 引用自己。
这个设计的目标是
Foo
的 objectFooEventHandler
的内部 object 包含对Foo
的引用FooEventHandler
传递给EventEmitter
以便它可以在事件上调用Foo
函数我提供了最少量的代码来同时说明目标和问题。 迄今为止,我的Event
模块没有任何问题,包括我的扩展EventHandler
引用其父对象(在本例中为Foo
)并将其发送到EventEmitter
以便它可以调用任何Foo
function 的范例,有点像lambda function 的实现。
然而,在使用这种设计大约一年后,当我需要执行诸如foo1 = foo2
(= operator) 或Foo foo1 = foo2
(copy constructor) 之类的事情时,我遇到了一个主要障碍。 我遇到了无法分配引用的问题( FooEventHandler
对Foo
的引用)。 所以我试图通过编写手动复制ctor和=
运算符来解决这个问题,现在我陷入了=
运算符的无限循环。
当我挖掘这个时,我什至不知道我想要完成什么,更不用说如何解决它了。 =
运算符的一个目的是当我想通过简单地用新的Foo
Foo
时,例如foo1 = foo2
。 但是,我正在转动我的轮子,试图弄清楚我想用Foo
的EventHandler
做什么。 foo1
的EventHandler
仍然应该引用自己,所以也许在=
运算符中我没有重新分配EventHandler
.. 但是,也许我这样做是因为foo1
应该是=
到foo2
,其EventHandler
引用foo2
...或者可能不是。? 或者是的??!
我希望有人可以看看这个问题,让我清楚我应该做什么。
备注:我在c++ 98
#include <iostream>
#include <string>
#include <vector>
// EventHandler and EventEmitter are just included to display my intent
class EventHandler {
public:
virtual ~EventHandler(){}
virtual void HandleEvent(/*some event*/) = 0;
};
class EventEmitter {
public:
std::vector<EventHandler*> handlers;
void AddHandler(EventHandler *handler){
this->handlers.push_back(handler);
}
void EmitEvent(/*some event*/){
for(size_t i = 0; i < this->handlers.size(); i++){
this->handlers.at(i)->HandleEvent(/*some event*/);
}
}
};
// The problem arises in Foo/FooEventHandler with circular references
class Foo {
public:
// This object is designed to carry Foo to the EventEmitter
class FooEventHandler : public EventHandler {
public:
Foo &foo;
FooEventHandler(Foo &foo)
:EventHandler(),
foo(foo)
{
printf("FooEventHandler CONSTRUCTOR\n");
}
FooEventHandler(const FooEventHandler &event_handler)
:EventHandler(),
foo(event_handler.foo)
{
printf("FooEventHandler COPY\n");
}
FooEventHandler operator=(const FooEventHandler& event_handler) {
printf("FooEventHandler =\n");
this->foo = event_handler.foo;
}
~FooEventHandler(){
printf("FooEventHandler DESTRUCTOR\n");
}
void HandleEvent(/*some event*/){
this->foo.HandleSomeEvent();
}
};
// Foo is just some generic object with a custom handler to ref itself
FooEventHandler event_handler;
Foo(std::string name)
:event_handler(*this)
{
printf("Foo CONSTRUCTOR\n");
}
Foo(const Foo &foo)
:event_handler(foo.event_handler)
{
printf("Foo COPY\n");
}
Foo operator=(const Foo& foo)
{
printf("Foo =\n");
this->event_handler = foo.event_handler;
}
~Foo(){
printf("Foo DESTRUCTOR\n");
}
void HandleSomeEvent(/*some event*/){
printf("Look at me handling an event");
}
};
int main()
{
printf("Foo1 create\n");
Foo foo1("a");
printf("Foo2 equal\n");
Foo foo2("b");
// start infinite loop of ='s
foo2 = foo1;
}
XY 回答:通过杀死问题的根源来解决整个问题 go:单独的事件处理程序 class。
进来boost::function
提供一个通用的 function 接口,用于事件回调(几乎)直接到Foo
和boost::bind
以抽象出Foo
-ness。
请注意,这使用了 Boost,而 Boost 安装起来很麻烦。 如果您的操作系统或开发环境的 package 管理器已准备好 go,不妨使用它。 如果没有,请放心使用function
并bind
为仅头文件库,并且比需要编译的库更容易工作。
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <boost/function.hpp>
#include <boost/bind.hpp>
template<class T> // now a template to allow for different types of events
class EventEmitter
{
public:
typedef boost::function<void(T&)> handler;
std::vector<handler> handlers;
void AddHandler(handler handle)
{
handlers.push_back(handle);
}
void EmitEvent(T& evt)
{
// No range-for. Oh well. Still no need for at. The loop bounds make
// overrun impossible
for (size_t i = 0; i < handlers.size(); i++)
{
handlers[i](evt); // call the function
}
}
};
class Foo
{
private:
// we can hide the event handlers away from prying eyes.
void HandleEvent(std::string &)
{
printf("Look at me handling a string event\n");
}
public:
// Foo might as well register itself on construction, so passing in the emitter
Foo(EventEmitter<std::string> & emitter,
std::string /*name*/)
{
// Bind this and the handler function together
emitter.AddHandler(boost::bind(&Foo::HandleEvent, this, _1));
printf("Foo CONSTRUCTOR\n");
}
~Foo()
{
printf("Foo DESTRUCTOR\n");
}
// but if we want to install event handlers later, here's a public function
void HandleEvent2(std::string &)
{
printf("Look at me handling a different string event\nOK. It's the same event, but it proves the point.\n");
}
};
int main()
{
printf("Foo1 create\n");
EventEmitter<std::string> test;
Foo foo(test, "a");
// same as above, but this time with foo in place of this
test.AddHandler(boost::bind(&Foo::HandleEvent2, &foo, _1));
std::string event;
test.EmitEvent(event);
}
失败的尝试:
Foo
包含一个EventHandler
,它需要知道拥有的Foo
才能调用它。 但是如果Foo
是EventHandler
呢? Foo
实现EventHandler
消除了问题的循环性质,因为没有人像Foo
那样了解Foo
。
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
// EventHandler and EventEmitter are just included to display my intent
class EventHandler {
public:
virtual ~EventHandler(){}
virtual void HandleEvent(/*some event*/) = 0;
};
class EventEmitter {
public:
std::vector<EventHandler*> handlers;
void AddHandler(EventHandler *handler){
this->handlers.push_back(handler);
}
void EmitEvent(/*some event*/){
for(size_t i = 0; i < this->handlers.size(); i++){
this->handlers.at(i)->HandleEvent(/*some event*/);
}
}
};
// Foo IS the FooEventHandler
class Foo: public EventHandler {
public:
void HandleEvent(/*some event*/){
// doesn't need to know Foo. It IS Foo
printf("Look at me handling an event\n");
}
Foo(std::string /*name*/)
{
printf("Foo CONSTRUCTOR\n");
}
~Foo(){
printf("Foo DESTRUCTOR\n");
}
};
int main()
{
printf("Foo1 create\n");
Foo foo1("a");
// start infinite loop of ='s
EventEmitter test;
test.AddHandler(&foo1);
test.EmitEvent();
}
整个问题就消失了,给你留下了更简单的代码。 保留这个,因为它真的很简单。
但它不会满足提问者的需求。 我在重新阅读问题和 C++98 要求之前写了这个。 它使用std::function
进行事件回调,并使用Lambda 表达式替换boost::bind
。
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <functional>
template<class T> // now a template to allow for different types of events
class EventEmitter
{
public:
using handler = std::function<void(T&)>; // simplify naming
std::vector<handler> handlers;
void AddHandler(handler handle)
{
handlers.push_back(handle);
}
void EmitEvent(T& evt)
{
// range-based for loop. Can't go out of bounds
for (const auto & handle : handlers)
{
handle(evt); // call the function
}
}
};
class Foo
{
private:
// we can hide the event handlers away from prying eyes.
void HandleEvent(std::string &)
{
printf("Look at me handling a string event\n");
}
public:
// Foo might as well register itself on construction, so passing in the emitter
Foo(EventEmitter<std::string> & emitter,
std::string /*name*/)
{
// install lambda expression as handler function
// lambda captures this so it knows which Foo to call
emitter.AddHandler([this](std::string& evt)
{
HandleEvent(evt); //call wrapped function
return;
});
printf("Foo CONSTRUCTOR\n");
}
~Foo()
{
printf("Foo DESTRUCTOR\n");
}
// but if we want to install event handlers later, here's a public function
void HandleEvent2(std::string &)
{
printf("Look at me handling a different string event\nOK. It's the same event, but it proves the point.\n");
}
};
int main()
{
printf("Foo1 create\n");
EventEmitter<std::string> test;
Foo foo(test, "a");
// install outside of foo
// lambda captures foo by reference. We don't want to operator on a copy
test.AddHandler([&foo](std::string& evt)
{
foo.HandleEvent2(evt); // wrap public handler function
return;
});
std::string event;
test.EmitEvent(event);
}
不是那么简单,但处理多种类型的事件注定会更加复杂。 保留,因为提问者不是唯一的人。
这是无限循环。 这些函数相互调用。
Foo(const Foo &foo) :event_handler(foo.event_handler)
{
printf("Foo COPY\n");
}
FooEventHandler(const FooEventHandler &event_handler)
:EventHandler(),
foo(event_handler.foo)
{
printf("FooEventHandler COPY\n");
}
我认为 FooEventHandler 不应该从抽象方面引用 Foo 。 你应该改变你的设计。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.