QVariant является оберткой, в которую можно завернуть объект любого типа. Он представляет собой обходное решение для языка C++, который имеет строгую типизацию.
В самом QtSDK этот класс применяется там, где нежелательна привязка к конкретному типу:
- QSettings;
- Моделях;
- При работе с базами данных.
Принцип работы QVariant
Для большинства примитивных типов C++ и встроенных классов Qt предусмотрены конструкторы QVariant:
1 2 3 |
QVariant var1( 5 ); QVariant var2( "Hello" ); QVariant var3( QRect( 5, 5, 10, 10 ) ); |
Распаковать значения можно с помощью специальных функций-членов:
1 2 3 |
qDebug() << var1.toInt(); // --> 5 qDebug() << var2.toString(); // --> "Hello" qDebug() << var3.toRect(); // --> QRect(5,5 10x10) |
Другой способ заключается в использовании шаблонной функции value< T >():
1 2 3 |
qDebug() << var1.value< int >(); // --> 5 qDebug() << var2.value< QString >(); // --> "Hello" qDebug() << var3.value< QRect >(); // --> QRect(5,5 10x10) |
Если перепутать запрашиваемый при распаковке тип, то в большинстве случаев будет возвращено значение по умолчанию.
Шаблонная функция распаковки значений QVariant
Сначала лучше проверить, что состояние обертки в порядке и конвертация допустима. Можно использовать подобную вспомогательную шаблонную функцию:
1 2 3 4 5 6 7 |
template< typename T > T unpack( const QVariant& var, const T& defVal = T() ) { if( var.isValid() && var.canConvert< T >() ) { return var.value< T >(); } return defVal; } |
Если преобразование не удалось, то unpack() вернет предусмотренное значение по умолчанию. Например:
1 2 |
// В var2 находится QString --> возвращает значение по умолчанию qDebug() << unpack( var2, QRect( 1, 1, 1, 1 ) ); // --> QRect(1,1 1x1) |
Пользовательский тип в QVariant
Чтобы поместить в QVariant свой собственный класс MyClass, необходимо:
- Наличие в MyClass конструктора по умолчанию;
- Наличие в MyClass конструктора копирования;
- Наличие в MyClass деструктора;
- Объявление мета-типа с помощью макроса Q_DECLARE_METATYPE( MyClass ).
Пример такого класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class MyClass { public: MyClass() : m_x( 0 ) { } MyClass( int x ) : m_x( x ) { } // Конструктор копирования и деструктор за нас создаст компилятор int get() const { return m_x; } private: int m_x; }; Q_DECLARE_METATYPE( MyClass ) |
Использовать его с QVariant не сложнее, чем со стандартными классами:
1 2 3 |
QVariant var4 = QVariant::fromValue( MyClass( 94 ) ); // Используем нашу функцию unpack() qDebug() << unpack< MyClass >( var4 ).get(); // --> 94 |
Динамические структуры на основе QVariant
С помощью QVariant можно создавать удивительно гибкие классы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class VariableContainer { public: template< typename T > void set( const QString& key, const T& val ) { m_vals[ key ] = QVariant::fromValue( val ); } template< typename T > T get( const QString& key, const T& defVal = T() ) { if( m_vals.contains( key ) ) { return unpack< T >( m_vals[ key ], defVal ); } return defVal; } private: QHash< QString, QVariant > m_vals; }; |
VariableContainer позволяет определять динамические структуры с произвольным количеством и содержанием полей:
1 2 3 4 5 6 7 |
container.set( "A", 5 ); container.set( "B", 8.4 ); container.set( "C", QPoint( 3, 6 ) ); qDebug() << container.get< int >( "A" ); // --> 5 qDebug() << container.get< float >( "B" ); // --> 8.4 qDebug() << container.get< QPoint >( "C" ); // --> QPoint(3,6) |