简体   繁体   中英

Qt:Signals and Slots vs C++:Message Passing

I have two classes

class Car
{
// class Car related methods
public:
setDriversName(QString);
private:
String driversName_;    // This information comes from class DriversName

//Must get the drivers name info, when a particular event in this object occurs;
}

class DriversName
{
//class DriversName related methods
String getDriversName();
private:
String driversName_;
}

So, now i want to communicate between the two classes, more specifically i want class Car to get the driver's name from the DriversName class when a particular event occours in Car Object .

So far i have these two ways,

C++ Message passing:

class Car
{
// class Car related methods
public:
setDriversNameObject(DriversName& );
setDriversName()
{
   driversName_ =  DriversName.getDriversName();
}

private:
DriversName driversName_;    // So after setting the Driverclass object, i can directly 
//access the driver's name, whenever the event occurs
String driversName_;
}

class DriversName
{
//class DriversName related methods
String getDriversName();

private:
DriversName driversName_;
}

OR

Qt

In mainWindow class:

connect(carObject,eventHasOccured(),this,updateDriversName());


void MainWindow::updateDriversName()
{
    carObject->setDriversName(driversNameObject.getDriversName);
}

Class Car
{
    Q_OBJECT
    signals:
    emit eventHasOccured();

    public:
    setDriversNameObject(DriversName& );

    private:
    DriversName driversName_;              
}

Class DriversName
{
    Q_OBJECT

    String getDriversName();

    private:
    DriversName driversName_;    
}

Both the Solutions will definitely work. Here are my questions:

1) Is there any flaw in the above methods, with respect to OO principles.

2) Which is the most standard way this situation can be dealt with ?

3) is there any other better method to handle the given situation.

Thanks.

1) Is there any flaw in the above methods, with respect to OO principles.

The first approach couples the Car method closely to the DriversName class.

The second couples your application to Qt. If you use it throughout your whole application anyway, that's not much of a problem, but it should be considered. Also, it moves business logic code into the mainwindow class, which might not be such a good idea.

2) Which is the most standard way this situation can be dealt with ?

There is no "one definitive way" of doing this. Just better and worse ones.

3) is there any other better method to handle the given situation.

For the "pure C++" approach, you could introduce an abstract interface for listening to driver name changes (or maybe even more generically for listening to generic name changes). What I'm referring to here is basically implementing the Observer pattern. A simplified implementation could eg look like this:

class NameChangeListener
{
public:
    virtual void onNameChange(std::string const & newName) =0;
};

// This assumes change is initiated in DriversName class - not sure if this is correct
// Your code doesn't show the actual triggering of the change
class DriversName
{
public:
// ... other stuff
    setNameChangeListener(NameChangeListener* ncl)
    {
        changeListener = ncl;
    }
    void setName(std::string const & newName)
    {
        // ... other stuff
        if (changeListener)
            changeListener->onNameChange(newName);
    }
private:
    // ..
    NameChangeListener* changeListener;
};

class Car: public NameChangeListener
{
public:
// ... other stuff
    void onNameChange(std::string const & newName) {
        driversName = newName;
    }
};

// somewhere outside:
Car c1;
DriversName n;
n.setNameChangeListener(c1);

For the Qt approach, it might be good to introduce an additional "Controller" class encapsulating such relations, instead of doing it directly in the main window.

A few more details on top of nyarlathotep's answer:

1) If you definitely need the update to take place when a particular event occurs , then the first solution is not enough because it does not handle events; one should explicitly call Car 's setDriversName() . You do need a signal/slot, or callback mechanism in general, like the second solution. It that case, I think you would rather need something like

connect(driversNameObject,nameChanged(string),carObject,setDriversName(string));

where neither the application/main window nor the carObject need to know anything about a driversNameObject . Besides that, I think there are a number of little errors in your second solution (eg, there is no setDriversName method in Car .

2) and 3) I would personally avoid QT's signal/slot mechanism if I am not already working in a QT project. This mechanism bypasses the C++ language entirely and needs source file preprocessing by its own tools. One consequence is that everything (like method names) is processed as a string, before the compiler has any chance for checking. Another is that you need a specific way to build your project that is way more complicated than necessary and would make the use of other libraries more difficult.

As for more "pure C++" solutions, I bet eg Boost.Signals would do the job, but again it would probably bring too many complexities that you don't need.

I have developed a pure-C++ abstract interface that mimics exactly the behavior of QT signals/slots based on delegates , but this involves low-level stuff internally like casting to void* . An alternative would be of course to use std::function in the internals. It is still not completely generic in handling all function-call language features, eg methods in derived classes, overloading, virtual methods, default arguments, and so on (but neither is Boost.Signals). I intend to re-implement it in C++11 using variadic templates before completing it.

Again, I am (still) not aware of a perfect or de facto standard solution.

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