Agregacja przy dziedziczeniu po QObject a wywoływanie funkcji składowych.

0

<inb4 - newbie> Mam dwie klasy dziedziczace po QObject. Pierwsza Contact, druga ContactList. Funkcja dołączająca Contact-y do ContactList ma formę:

void ContactList::add(QStringList data){
    new Contact(data, this);
}

Wewnątrz Klasy ContactList staram się dotrzeć do obiektów składowych, np:

auto childList = this->children();
for(auto wej : childList)
{
	qDebug() << wej->objectName() << endl; // działa
}

Wszystko idzie ok, ContactList ma dostęp do wszystkich funkcji składowych, jednak obiekty z childList dysponują tylko funkcjami z QObject (a chciałbym dobrać się do funkcji składowych klasy Contact). Jakie zmiany muszę wprowadzić, aby dostać się do tych funkcji?

Całość kodu poniżej

#include <QObject>
#include <QList>
#include <QDebug>

class Contact : public QObject
{
public:
    friend bool operator==(Contact L, Contact R);

    explicit Contact(QStringList wej, QObject* parent = 0)
           :m_Category(     wej[0].toInt()) ,
            m_FirstName(    wej[1]),
            m_LastName(     wej[2]),
            m_StreetAdress( wej[3]),
            m_ZipCode(      wej[4]),
            m_City(         wej[5]),
            m_PhoneNumber(  wej[6]),
            m_EMail(        wej[7]),
            QObject(parent) { this->setObjectName(m_FirstName + " " + m_LastName); }

    virtual ~Contact(){}
    QString toQString();

private:
    int m_Category;
    QString m_FirstName;
    QString m_LastName;
    QString m_StreetAdress;
    QString m_ZipCode;
    QString m_City;
    QString m_PhoneNumber;
    QString m_EMail;
};

QString Contact::toQString(){
    QString sep = "[::]";
    QString cat = QString::number(m_Category);
    return  cat + sep +
            m_FirstName + sep +
            m_LastName + sep +
            m_StreetAdress + sep +
            m_ZipCode + sep +
            m_City + sep +
            m_PhoneNumber + sep +
            m_EMail;
}

bool operator==(Contact L, Contact R){
    if(L.m_FirstName == R.m_FirstName && L.m_LastName == R.m_LastName && L.m_PhoneNumber == R.m_PhoneNumber)
        return 1;
    else return 0;
}


class ContactList : public QObject
{
public:
    void add(QStringList data);
    void remove(Contact c);
    QStringList getPhoneList(int category);
    QStringList getMailingList(int category);
    ContactList(QString ListName = "Lista Kontaktow") {this->setObjectName(ListName);}
private:
    void getData(QStringList& Out, QString sep, int category, int additionalData);
};


void ContactList::add(QStringList data){
    new Contact(data, this);
}


QStringList ContactList::getPhoneList(int category){
    QStringList Out;
    QString sep = " ";

    auto childList = this->children();
    for(auto wej : childList)
    {
        qDebug() << wej->objectName() << endl;
    }

    return Out;
}
1

Jeśli jesteś przekonany, że wszystkie są odpowiedniego typu, to static_cast<Contact*>(wej). Jeśli nie to użyj qobject_cast lub dynamic_cast aby sprawdzić dynamiczny typ obiektu. Ogólnie takie użycie to antyidiom (nie wydaje mi się abyś powinien do tego wykorzystywać poniekąd wewnętrzne składowe QObject), ale w tej sytuacji chyba sensowne jeśli nie chcesz robić dużych modyfikacji w kodzie ( @MarekR22 jak coś to mnie popraw ad. sensowności ). Alternatywnie możesz trzymać osobną listę Contact*.

Przy okazji: kompiluje Ci się to w ogóle? Contact::operator== i ContactList::remove przyjmują Contact przez kopię, co powinno być niemożliwe dla klas dziedziczących z QObject (o ile się nie mylę).

0

Wielkie dzięki. Podany sposób wykorzystujący rzutowanie działa. Co do samego programu to Contact::operator== i ContactList::remove nie są jeszcze zaimplementowane ani używane, więc kompilator nic nie krzyczy (muszę je po prostu przerobić).

Jeżeli chodzi o podejścia i jego sensowność - robię zadanie("C++ i Qt. Wzorce projektowe" <s. 261>), więc jeśli autor miał taki zamysł, to pewnie stał za nim jakiś cel :) Treść polecenia:

Przepisz klasy Contact i ContactList tak, by dziedziczyły z QObject.
Gdy kontakt jest dodawany do listy, zadbaj o to, by stał się jej dzieckiem.

w oparciu o kod napisany do poprzedniego zadania - https://pastebin.com/xbKwNKkR

Założyłem że ogólne relacje między klasami mają pozostać takie jak wcześniej, z tym że obie klasy muszę dostosować do specyfiki bycia rozszerzeniem QObject;
Jeśli źle zrozumiałem polecenie, ew. istnieje droga bez antyidiomów, to mogę prosić o sugestie / słowa klucze? Oczywiście bez gotowych rozwiązań.

3

IMO cały design jest dziwny. Sama pomysł agregacji QObject jest podejrzany, a używanie do tego relacji child-parent to komplikowanie sobie życia.

Patrząc po samych nazwach klas to powinno iść tak (zakładając, że potem chcesz pokazać to w jakiejś tabeli:

class Contact // to powinien być "value object", zero callbaków, slotów, sygnałów 
// klasa powinna być kopiowalna i nie mieć metod wirtualnych - brak polimorfizmu
{
public:
    // rule of zero

   void Serialize(QIODevice *dev); // zapisz dane do struminia
   void Deserialize(QIODevice *dev); // wczytaj dane ze strumienia
   // albo wersja z  QTextStream albo jeszcze z czyś innym

   boo isEqualTo(const Contact& other) const;

   QString name() const;
   void setName(const QString& name);

   // TODO: inne accessory
 
private:
    int m_Category = 0;
    QString m_FirstName;
    QString m_LastName;
    QString m_StreetAdress;
    QString m_ZipCode;
    QString m_City;
    QString m_PhoneNumber;
    QString m_EMail;
};

class ContactListTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
    explicit ContactListTableModel(QObject* parent);

    bool Serialize(const QString &fileName);
    bool Serialize(QIODevice * device);
    bool Deserialize(const QString &fileName);
    bool Deserialize(QIODevice * device);

    auto rowCount(const QModelIndex &parent) const -> int;
    auto columnCount(const QModelIndex &parent) const -> int;
    auto data(const QModelIndex &index, int role) const -> QVariant;
    auto setData(const QModelIndex &index, const QVariant &value, int role) -> bool
    {
           if (role != Qt::EditRole) {
                 return false;
           }
           auto row = index.row();
           if (row >= m_Contacts.count() || row < 0) {
               return false;
           }
           Contact& contact = m_Contacts[row];
           switch(index.column())
           {
           case 0:
                 contact.setFirstName(value.toString());
                 break;
           … … …
           default:
                return false;
           }
           emit dataChanged(index);
           return true;
    }

    auto flags(const QModelIndex &index) const -> Qt::ItemFlags
    {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren;
    }
    auto headerData(int section, Qt::Orientation orientation, int role) const -> QVariant;

private:
    QVector<Contact> m_Contacts;
};

Disclaimer: pisane na sucho z pamięci, wiec będą literówki i inne braki, chodzi o generalny koncept jak takie rzeczy robi się Qt.

1

Witam,
Żadnych rzutowań. Qt wyposażone jest w qtproperty system. Jeśli masz obiekt dziedziczący po qobject możesz dostać się do metod. Zastanów się czy to spełnia twoje założenia link poniżej
http://doc.qt.io/qt-5/properties.html

Bo jak zrozumiałem mając tylko qobject chcesz wywołać odpowiednie metody.

1 użytkowników online, w tym zalogowanych: 0, gości: 1