多态队列

时间:2010-04-08 09:21:30

标签: c++ queue polymorphism smart-pointers

我正在尝试实现多态队列。 这是我的试用版:

QQueue <Request *> requests;

while(...)
    {
        QString line = QString::fromUtf8(client->readLine()).trimmed();

        if(...)){                      
            Request *request=new Request();
            request->tcpMessage=line.toUtf8();
            request->decodeFromTcpMessage(); //this initialize variables in request using tcpMessage
            if(request->requestType==REQUEST_LOGIN){
                LoginRequest loginRequest;
                request=&loginRequest;
                request->tcpMessage=line.toUtf8();
                request->decodeFromTcpMessage();
                requests.enqueue(request);
            }
            //Here pointers in "requests" do not point to objects I created above, and I noticed that their destructors are also called.
            LoginRequest *loginRequest2=dynamic_cast<LoginRequest *>(requests.dequeue());   
            loginRequest2->decodeFromTcpMessage();
        }
    }

不幸的是,由于我在第二条评论中提到的原因,我无法使用此代码设置工作多态队列。我猜,我需要使用智能指针,但是如何? 我愿意改进我的代码或多态队列的新实现。

感谢。

4 个答案:

答案 0 :(得分:2)

您将无效指针指向QQueue。如果QQueue保存指针,则需要创建在堆上插入的每个对象,即调用new。此外,如果您不需要,请不要忘记释放第一个创建的Request

我认为您应该将代码重写为:

...
if(request->requestType==REQUEST_LOGIN){
    delete request;
    request = new LoginRequest();
    request->tcpMessage=line.toUtf8();
    ...
}

使用此代码,您之后的dynamic_cast<LoginRequest*>不会失败。

答案 1 :(得分:2)

您的来源有两个问题:

  • 您通过Request *request=new Request();声明了内存,后者被request=&loginRequest;分配放弃(并且不再可删除)
  • 当执行离开定义变量的{}块时LoginRequest loginRequest;变量被破坏,导致 request中的悬空指针

我建议删除Request *request=new Request();行,稍后在if(...){块中指定具体的LoginRequest对象

LoginRequest* loginRequest = new LoginRequest();
/* fill the request */
requests.enqueue(loginRequest);

您可以通过在排队队列(从处理完毕后)或在队列中使用容器安全智能指针时手动删除它们来删除排队对象。 :: shared_ptr很好,也许QT也有其中一个, std :: auto_ptr不是容器安全的)。

PITFALL 还要确保Request的析构函数是虚拟的,因为当基类中没有虚拟析构函数时,你不能通过指向其基本classe的指针删除对象(c ++可以调用基类)在这种情况下使用派生类实例的类析构函数,导致未定义的行为,如内存泄漏或崩溃)

答案 2 :(得分:1)

立即从代码片段中我可以看到Request的对象排队,稍后您尝试将其向下转发到LoginRequest。 dynamic_cast肯定会失败。您必须解析请求数据并创建从Request派生的适当类的对象。我建议使用Factory Pattern。

答案 3 :(得分:1)

这也是使用工厂的好地方,IMO。

if(...)){
   Request *request = CreateRequest(message);
   requests.enqueue(request);
}

Request* request = requests.pop();
LoginRequest* login_req = dynamic_cast<LoginRequest*>(request);
if (login_req != NULL)
   whatever;

,其中

Request* CreateRequest(TcpMessage* message)
{
   TcpMessage* message = line.toUtf8();
   RequestType type = message->GetRequestType();
   Request* req = NULL;

   switch (type)
   {
   case REQUEST_LOGIN:
      req = new LoginRequest(message);
      break;
   etc.
   }

   delete message;
   return req;
}

...然后,您的构造函数自然会对消息做正确的事情,正确初始化对象。