2010-04-08 7 views
1

J'essaie de mettre en œuvre une file d'attente polymorphe. Voici mon procès:File d'attente polymorphe

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(); 
     } 
    } 

Malheureusement, je ne parvenais pas à faire le travail polymorphes file d'attente avec ce code en raison de la raison pour laquelle je l'ai mentionné dans guess deuxième comment.I, je dois utiliser Smart-pointeurs, mais comment ? Je suis ouvert à toute amélioration de mon code ou à une nouvelle implémentation de la file d'attente polymorphe.

Merci.

+0

C'est faux à bien des égards, je ne sais même pas par où commencer. Pour commencer: Pourquoi convertissez-vous d'abord l'entrée en UTF-8, puis revenez en UTF-8? Pourquoi 'decodeFromTcpMessage()' n'est pas une fonction libre qui prend une chaîne et renvoie une requête allouée dynamiquement? Vous mettez en file d'attente l'adresse d'un objet automatique local dans la file d'attente. (Aïe!) Vous essayez toujours de récupérer un 'LoginRequest', même si vous mettez aussi d'autres dans la file d'attente. Vous accédez au résultat de 'dynamic_cast' sans vérifier si les conversions ont réussi. (Pourquoi lancez-vous de toute façon? Quel est le problème avec les fonctions virtuelles?) ... – sbi

+0

... J'ai manqué d'espace, car un commentaire ne permet que 600 caractères. Je vous suggère de vous procurer un livre décent en C++. Par exemple, voir ici: http://stackoverflow.com/questions/388242/. – sbi

Répondre

2

Il y a 2 problèmes dans votre source:

  • vous prétendez mémoire par le Request *request=new Request();, qui obtient abandonné par la suite request=&loginRequest; cession (et est plus supprimable)
  • la variable LoginRequest loginRequest; se destructed lorsque la exécution quitte le {} bloc où la variable est définie, ce qui entraîne un pointeur ballants dans request

Je suggère d'enlever le Request *request=new Request(); ligne, et plus tard dans le bloc if(...){ affecter l'objet LoginRequest béton par

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

Vous pouvez vous débarrasser des objets en file d'attente en les supprimant manuellement quand ils se poped de la file d'attente (après leur traitement), ou en utilisant un conteneur de sécurité dans la pointeur intelligent file d'attente (boost :: shared_ptr est très bien, QT a peut-être l'un d'entre eux, std :: auto_ptr eST conteneur sécurité).

PIÈGE Assurez-vous également que le destructor de la demande est virtuelle, puisque vous ne pouvez pas supprimer des objets par un pointeur vers sa base classe lorsqu'il n'y a pas destructor virtuelle dans la classe de base (C++ peut appeler la classe de base destructor avec le instance de classe dérivée dans ce cas, menant dans un comportement indéfini comme des fuites de mémoire ou des accidents)

1

immédiatement à partir du code snippet je peux voir qu'un objet de la demande est mise en attente et plus tard vous essayez de downcaster à LoginRequest. dynamic_cast échouera légitimement. Vous devez analyser les données de requête et créer des objets de la classe appropriée, dérivés de Request. Je suggère d'utiliser Factory Pattern pour cela.

2

Vous placez un pointeur non valide sur votre QQueue. Si votre QQueue contient des pointeurs, vous devez créer chaque objet que vous insérez sur le tas, c'est-à-dire avec un appel à new. Aussi, n'oubliez pas de libérer le premier Request créé si vous n'en avez pas besoin.

Je pense que vous devez réécrire votre code:

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

Avec ce code, votre plus tard dynamic_cast<LoginRequest*> ne manquera pas.

1

C'est aussi un bon endroit pour utiliser des usines, 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; 
} 

... et puis, naturellement, vos constructeurs font la bonne chose avec le message, l'initialisation des objets correctement.

Questions connexes