QVariant
является оберткой, в которую можно завернуть объект любого типа. Он представляет собой обходное решение для языка C++, который имеет строгую типизацию.
В самом QtSDK этот класс применяется там, где нежелательна привязка к конкретному типу:
Для большинства примитивных типов C++ и встроенных классов Qt предусмотрены конструкторы QVariant
:
QVariant var1( 5 );
QVariant var2( "Hello" );
QVariant var3( QRect( 5, 5, 10, 10 ) );
Распаковать значения можно с помощью специальных функций-членов:
qDebug() << var1.toInt(); // --> 5
qDebug() << var2.toString(); // --> "Hello"
qDebug() << var3.toRect(); // --> QRect(5,5 10x10)
Другой способ заключается в использовании шаблонной функции value< T >()
:
qDebug() << var1.value< int >(); // --> 5
qDebug() << var2.value< QString >(); // --> "Hello"
qDebug() << var3.value< QRect >(); // --> QRect(5,5 10x10)
Если перепутать запрашиваемый при распаковке тип, то в большинстве случаев будет возвращено значение по умолчанию.
Сначала лучше проверить, что состояние обертки в порядке и конвертация допустима. Можно использовать подобную вспомогательную шаблонную функцию:
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()
вернет предусмотренное значение по умолчанию. Например:
// В var2 находится QString --> возвращает значение по умолчанию
qDebug() << unpack( var2, QRect( 1, 1, 1, 1 ) ); // --> QRect(1,1 1x1)
Чтобы поместить в QVariant
свой собственный класс MyClass
, необходимо:
MyClass
конструктора по умолчанию;MyClass
конструктора копирования;MyClass
деструктора;Q_DECLARE_METATYPE( MyClass )
.Пример такого класса:
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
не сложнее, чем со стандартными классами:
QVariant var4 = QVariant::fromValue( MyClass( 94 ) );
// Используем нашу функцию unpack()
qDebug() << unpack< MyClass >( var4 ).get(); // --> 94
С помощью QVariant
можно создавать удивительно гибкие классы:
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
позволяет определять динамические структуры с произвольным количеством и содержанием полей:
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)
Mikhail
Здравствуйте. Спасибо за комментарий.
Идея использования QVariantMap звучит вполне разумно, хотя полагаю, что можно обойтись и без этого. То, о чем Вы рассуждаете, очень близко по духу к паттерну Компоновщик. Можете ознакомиться с моими статьями на эту тему: Паттерн Компоновщик на C++, Паттерн Абстрактная фабрика на C++ и Паттерн Посетитель на C++.
Конечно, в указанных статьях рассмотрены частные случаи, но они могут послужить пищей для размышлений в контексте Вашей задачи.