简体   繁体   中英

Qt Signals: Passing ownership of dynamically allocated argument to a slot?

I don't know how to exactly title this question, so here is the explaination:

I have a custom widget that emits a signal with a custom object built from the user's input when finished with editing, eg

void GGActionEditor::finishEditing() {
  GGAction act;
  // Set up according to user input
  emit actionChanged(act);
}

In this code, receivers that are connected to the singal can store a copy of that object or process it in any way the see fit, and the widget can forget about it.

But how to handle such a situation when the widget can build different types of objects from a class hierachry? Like:

void GGActionEditor::finishEditing() {
  GGAction *pAct;
  // Create and set up according to user input, e.g.
  if (...) pAct = new GGSpecialAction;
  else pAct = new SimpleAction;
  emit actionChanged(pAct);
}

What is the best solution to handle the object's life cicle such a situation? The widget cannot know if any receiver takes over the Action object. Also, if multiple receivers are connected, none of them knows if another object has taken over the parameter...
In the "best" case, this can cause a leak; but it can also cause that the parameter will be deleted multiple times.

Update:
Some more information about my actual situation, however, I'm also interested in general solutions:

The passed object will be stored by a receiver and kept in the application's domain model. The model will take care of deleting it later.
The simple solution is to just handle this case like the "simple" by-value case described above. However, if there are no receivers connected at all, the object would leak; and there is no 100% guarantee that only 1 receiver will do this (in my actual application logic this will be the case, but it can neither be checked nor enforced in this way).

I came up with some possible solutions, and are interested in any comments, or additions:

  1. Just hope it will work out :
    Assume that there will be exactly 1 receiver that takes over the parameter. This might work well for my application, but I don't feel happy with it
  2. Use a shared pointer :
    As Eugene said in an answer, shared_ptr (or QSharedPointer) would simplify this. Any reciever and the sender would use a shared pointer, so the life cycle is managed automatically. But this forces my domain model to also use a shared pointer for every class that holds such a Action object. This does not fit well in my model, since those classes should aggregate the action. A shared pointer doesn't feel right for holding a reference to an object that "belongs" to the object...
  3. Add parameter indicating overtake :
    Add a bool* to the signal, that indicates if some receiver has taken over the action. A receiver knowns that it cannot overtake it anymore, if the value will be true. Also the sender knows if it should delete the object if no receiver has taken it. But it is quite random which receiver will take the object, in a first-come first-serve manner. And it polutes the interface...
  4. Use different signals for different concrete classes :
    In this case, the receiver's slots can know which concrete subclass is used and make a copy of the object. But this adds additional signals/slots for something that can be very well done using the class hierarchy... Also a copy-c'tor must be available for every subclass.
  5. Add a clone method to the classes :
    Similar to above, but only 1 signal. The polymorphic clone would create a copy of the concrete subclass.
  6. Use a specialized interface instead of signal/slot :
    Make a GGIActionReceiver class, and assign at most 1 instance in the GGActionEditor . This instance is the only eligable object to take over the Action object. Others can still be signalled, but they mustn't overtake the pointer. This way, the sender also knows if the object must be deleted (no receiver set)

One option here is to use shared ownership via std::shared_ptr<> or some other reference counting mechanism. This way the object will live as long as anybody needs it.

Qt's normal ownership model doesn't quite work because you can't be sure how many signal users will want to take ownership of the same object.

Depending on your logic shared ownership might lead to closed loops and to break those you might be forced to make holes in an abstraction or two.

Make sure not to mix Qt ownership with shared_ptr -- your QObjects must not have parents.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM