Search code examples
c++qtqobjectqlist

Creating QList of multiple object types


I need to make multiple instances of various class types (classA, classB, classC), and store then in a single list. Is is possible to create these objects dynamically and then add them to a single QList? Something like:

QList<QObject> mylist;
mylist.append(classA());
mylist.append(classB());
mylist.append(classC());

This won't compile because the objects are not of type QObject. I could static_cast them to QObject, but will I be able to typeid.name() to determine their origical types? (To cast them back later)?

I found an example here of using a QList< QVariant >, but the compiler complains that:

error: no matching function for call to 'QVariant::QVariant(classA&)'

for this code:

QList<QVariant> mylist;
mylist.append(QVariant(tempObj));

Update:

I got it compiling by creating the object during the append operation, AND using ::fromValue.

mylist.append(QVariant::fromValue(ClassA(1,2,3)));

Not sure this is right. 1 - will this cause a memory leak/problem creating the object in the append statement? 2 - why would I need ::fromValue to make this work? (Does this just make a second copy of the object?)

I put a qDebug in my constructors, and I see that my constructor with parameters is called twice, then the copy constructor is called once, and the default (no parameters) constructor is never called. I don't understand that...


Solution

  • You can use QVariant which can hold any number of different types: QVariant API

    To use QVariant with a custom type you need to register it with the metatype system: QMetaType This means using Q_DECLARE_METATYPE(MyClass) Q_DECLARE_METATYPE()

    QVariant is designed for holding objects of different types, and is used in all sorts of Qt conventions when the programmer wants to pass around user-defined types through Qt's interfaces when Qt itself has no way of knowing which type the programmer wants to use.

    To convert back to the original type use a combination of QVariant::canConvert() and QVariant::value():

    QVariant v;
    
    MyCustomStruct c;
    if (v.canConvert<MyCustomStruct>())
        c = v.value<MyCustomStruct>();
    
    v = 7;
    int i = v.value<int>();                        // same as v.toInt()
    QString s = v.value<QString>();                // same as v.toString(), s is now "7"
    MyCustomStruct c2 = v.value<MyCustomStruct>(); // conversion failed, c2 is empty
    

    Your custom type must provide:

    1. a public default constructor,
    2. a public copy constructor, and
    3. a public destructor.

    The following Message class is an example of an acceptable custom type for use in QVariant

    class Message
    {
     public:
       Message();
       Message(const Message &other);
       ~Message();
    
       Message(const QString &body, const QStringList &headers);
    
       QString body() const;
       QStringList headers() const;
    
     private:
       QString m_body;
       QStringList m_headers;
    };
    

    You could also declare your own "meta-type wrapper" that holds a type and a description of a type. The simplest example:

    template <typename T>
    class AnyType
    {
    public:
      enum THE_TYPE { MT_INT, MT_STRING };
    
      AnyType(T value, THE_TYPE type) : value(value), type(type) { }
      THE_TYPE getType() { return type; }
      T getValue() { return value; }
    
    private:
      T value;
      THE_TYPE type;
    }