Qt中如何根据类名来实例化对象

时间:2021-11-17 09:21:51
QWidget* createWidget(const Qstring& name);
类似这个的一个函数,如何通过一个字符串函数名来实例一个具体的对象,求大牛

9 个解决方案

#1


个人感觉qt的元数据系统应该提供了此项功能,类似mfc的dynamic create 不过没有研究过

#2


C++反射机制可以实现。

#3


int id = QMetaType::type("MyClass");
 if (id != 0) {
     void *myClassPtr = QMetaType::construct(id);
     ...
     QMetaType::destroy(id, myClassPtr);
     myClassPtr = 0;
 }

#4


http://bbs.csdn.net/topics/340251573

#5


引用 3 楼 heksn 的回复:
int id = QMetaType::type("MyClass");
 if (id != 0) {
     void *myClassPtr = QMetaType::construct(id);
     ...
     QMetaType::destroy(id, myClassPtr);
     myClassPtr = 0;
 }

如果我要实例的类是Qt本身的QObject或者QWidget的子类,例如QLineEdit,那QMetaType::type是0,就失效了

#6


引用 4 楼 dext 的回复:
http://bbs.csdn.net/topics/340251573

dext版大,那个帖子我详细看了,我想问下,如果是QObject或者QWidget的子类的话,我一定要把所有的子类都重新派生后注册一遍,有没有其他的一些办法

#7


因为我的className可能是Qt本身现有的一些控件类,也可能是一些自定义的控件类,对于自定义的话,可以用QMetaType来处理,但是自有类还是存在一些问题

#8


本帖最后由 dext 于 2013-06-07 05:39:58 编辑
既然是这样,我就索性把这个问题,全部的回答一下。

对于Qt 来说,是可以做到运行时,根据对象的类名字(字符串)来获得对象的实例的,这点和一些语言的反射机制是一样的。

但是在Qt中,我们需要所额外的一步,就是注册。
只要做到了注册,我们就可以 *的创建对象了。


#include <QtCore>

class Parser {
public:
    virtual void parse() = 0;
    virtual ~Parser() {}
};

class Parser1 : public Parser {
public:
    Parser1() {
        qDebug() <<"Parser1::Parser1()";
    }
    void parse() {
        qDebug() << "Parser1::parse()";
    }
    ~Parser1() {
       qDebug() <<"Parser1::~Parser1()";
    }
};
Q_DECLARE_METATYPE(Parser1)

class Parser2 : public Parser {
public:
    Parser2() {
        qDebug() <<"Parser2::Parser2()";
    }
    void parse() {
        qDebug() << "Parser2::parse()";
    }
    ~Parser2() {
       qDebug() <<"Parser2::~Parser2()";
    }
};
Q_DECLARE_METATYPE(Parser2)


void factory( const char* parserName ) {
     int id = QMetaType::type( parserName );
     if (id == -1) return; // ERROR HERE
     Parser *parser = static_cast<Parser*>(QMetaType::construct(id));
     parser->parse();
     delete parser;
}

int main () {
    qRegisterMetaType<Parser1>("Parser1");
    qRegisterMetaType<Parser2>("Parser2");

    qDebug() << "###### Trying create Parser1";
    factory("Parser1");
    qDebug() << "###### Trying create Parser2";
    factory("Parser2");
}


注册分为两步,定义的时候 使用 Q_DECLARE_METATYPE 宏。
使用的时候使用 qRegisterMetaType 函数
这样就可以在Qt 中动态地创建自己定义的类了。

但是这个方法,在注册 已有的 QWidget 和 QObject 的子类的时候会遇到问题。
原因是,在创建对象的时候,Qt 中有一个函数 qMetaTypeConstructHelper 会被调用,这个函数调用了欲创建类的复制构造函数,而复制构造函数 在 QWidget 和 QObject 的子类里是被禁用的。

解决的办法 就有两个,
一,启用 复制构造函数,
二,特化重写 qMetaTypeConstructHelper 函数。

复制构造函数 的启用 需要重新派生一个子类,就不多说了。
对于第二个方法,我们只要对于每一个特定的类 单独写一个函数就好了。如果使用宏,就会大大便捷这个过程。
代码如下。


#include <QtCore>

#define Q_DECLARE_QTBUILTIN_METATYPE(TYPE)          \
template <>                                         \
void *qMetaTypeConstructHelper(const TYPE *t) {     \
    Q_UNUSED(t)                                     \
    return new TYPE();                              \
}                                                   \
Q_DECLARE_METATYPE(TYPE)



Q_DECLARE_QTBUILTIN_METATYPE(QSettings)
Q_DECLARE_QTBUILTIN_METATYPE(QTimer)


void outputObject(const char* parseName) {
    QObject* obj = static_cast<QObject*>(QMetaType::construct(QMetaType::type(parseName)));
    qDebug() << obj;
    delete obj;
}

int main(int argc, char *argv[])
{
    qRegisterMetaType<QTimer>("QTimer");
    qRegisterMetaType<QSettings>("QSettings");

    outputObject("QTimer");
    outputObject("QSettings");

    QTimer* tmr = static_cast<QTimer*>(QMetaType::construct(QMetaType::type("QTimer")));
    tmr->setInterval(50000);
    qDebug() << tmr->interval();
    delete tmr;
    return 0;
}


引用 4 楼 dext 的回复:
dext版大,那个帖子我详细看了,我想问下,如果是QObject或者QWidget的子类的话,我一定要把所有的子类都重新派生后注册一遍,有没有其他的一些办法

#9


引用 8 楼 dext 的回复:
既然是这样,我就索性把这个问题,全部的回答一下。

对于Qt 来说,是可以做到运行时,根据对象的类名字(字符串)来获得对象的实例的,这点和一些语言的反射机制是一样的。

但是在Qt中,我们需要所额外的一步,就是注册。
只要做到了注册,我们就可以 *的创建对象了。


#include <QtCore>

class Parser {
public:
    virtual void parse() = 0;
    virtual ~Parser() {}
};

class Parser1 : public Parser {
public:
    Parser1() {
        qDebug() <<"Parser1::Parser1()";
    }
    void parse() {
        qDebug() << "Parser1::parse()";
    }
    ~Parser1() {
       qDebug() <<"Parser1::~Parser1()";
    }
};
Q_DECLARE_METATYPE(Parser1)

class Parser2 : public Parser {
public:
    Parser2() {
        qDebug() <<"Parser2::Parser2()";
    }
    void parse() {
        qDebug() << "Parser2::parse()";
    }
    ~Parser2() {
       qDebug() <<"Parser2::~Parser2()";
    }
};
Q_DECLARE_METATYPE(Parser2)


void factory( const char* parserName ) {
     int id = QMetaType::type( parserName );
     if (id == -1) return; // ERROR HERE
     Parser *parser = static_cast<Parser*>(QMetaType::construct(id));
     parser->parse();
     delete parser;
}

int main () {
    qRegisterMetaType<Parser1>("Parser1");
    qRegisterMetaType<Parser2>("Parser2");

    qDebug() << "###### Trying create Parser1";
    factory("Parser1");
    qDebug() << "###### Trying create Parser2";
    factory("Parser2");
}


注册分为两步,定义的时候 使用 Q_DECLARE_METATYPE 宏。
使用的时候使用 qRegisterMetaType 函数
这样就可以在Qt 中动态地创建自己定义的类了。

但是这个方法,在注册 已有的 QWidget 和 QObject 的子类的时候会遇到问题。
原因是,在创建对象的时候,Qt 中有一个函数 qMetaTypeConstructHelper 会被调用,这个函数调用了欲创建类的复制构造函数,而复制构造函数 在 QWidget 和 QObject 的子类里是被禁用的。

解决的办法 就有两个,
一,启用 复制构造函数,
二,特化重写 qMetaTypeConstructHelper 函数。

复制构造函数 的启用 需要重新派生一个子类,就不多说了。
对于第二个方法,我们只要对于每一个特定的类 单独写一个函数就好了。如果使用宏,就会大大便捷这个过程。
代码如下。


#include <QtCore>

#define Q_DECLARE_QTBUILTIN_METATYPE(TYPE)          \
template <>                                         \
void *qMetaTypeConstructHelper(const TYPE *t) {     \
    Q_UNUSED(t)                                     \
    return new TYPE();                              \
}                                                   \
Q_DECLARE_METATYPE(TYPE)



Q_DECLARE_QTBUILTIN_METATYPE(QSettings)
Q_DECLARE_QTBUILTIN_METATYPE(QTimer)


void outputObject(const char* parseName) {
    QObject* obj = static_cast<QObject*>(QMetaType::construct(QMetaType::type(parseName)));
    qDebug() << obj;
    delete obj;
}

int main(int argc, char *argv[])
{
    qRegisterMetaType<QTimer>("QTimer");
    qRegisterMetaType<QSettings>("QSettings");

    outputObject("QTimer");
    outputObject("QSettings");

    QTimer* tmr = static_cast<QTimer*>(QMetaType::construct(QMetaType::type("QTimer")));
    tmr->setInterval(50000);
    qDebug() << tmr->interval();
    delete tmr;
    return 0;
}


Quote: 引用 4 楼 dext 的回复:

dext版大,那个帖子我详细看了,我想问下,如果是QObject或者QWidget的子类的话,我一定要把所有的子类都重新派生后注册一遍,有没有其他的一些办法

谢谢dext
不过我在解决问题的时候,我有发现一个问题
我的是Qt5.0.2,会出现一个这样的error: 'qMetaTypeConstructHelper' is not a template function
我查了一些资料,还是没有找到解决的办法

#1


个人感觉qt的元数据系统应该提供了此项功能,类似mfc的dynamic create 不过没有研究过

#2


C++反射机制可以实现。

#3


int id = QMetaType::type("MyClass");
 if (id != 0) {
     void *myClassPtr = QMetaType::construct(id);
     ...
     QMetaType::destroy(id, myClassPtr);
     myClassPtr = 0;
 }

#4


http://bbs.csdn.net/topics/340251573

#5


引用 3 楼 heksn 的回复:
int id = QMetaType::type("MyClass");
 if (id != 0) {
     void *myClassPtr = QMetaType::construct(id);
     ...
     QMetaType::destroy(id, myClassPtr);
     myClassPtr = 0;
 }

如果我要实例的类是Qt本身的QObject或者QWidget的子类,例如QLineEdit,那QMetaType::type是0,就失效了

#6


引用 4 楼 dext 的回复:
http://bbs.csdn.net/topics/340251573

dext版大,那个帖子我详细看了,我想问下,如果是QObject或者QWidget的子类的话,我一定要把所有的子类都重新派生后注册一遍,有没有其他的一些办法

#7


因为我的className可能是Qt本身现有的一些控件类,也可能是一些自定义的控件类,对于自定义的话,可以用QMetaType来处理,但是自有类还是存在一些问题

#8


本帖最后由 dext 于 2013-06-07 05:39:58 编辑
既然是这样,我就索性把这个问题,全部的回答一下。

对于Qt 来说,是可以做到运行时,根据对象的类名字(字符串)来获得对象的实例的,这点和一些语言的反射机制是一样的。

但是在Qt中,我们需要所额外的一步,就是注册。
只要做到了注册,我们就可以 *的创建对象了。


#include <QtCore>

class Parser {
public:
    virtual void parse() = 0;
    virtual ~Parser() {}
};

class Parser1 : public Parser {
public:
    Parser1() {
        qDebug() <<"Parser1::Parser1()";
    }
    void parse() {
        qDebug() << "Parser1::parse()";
    }
    ~Parser1() {
       qDebug() <<"Parser1::~Parser1()";
    }
};
Q_DECLARE_METATYPE(Parser1)

class Parser2 : public Parser {
public:
    Parser2() {
        qDebug() <<"Parser2::Parser2()";
    }
    void parse() {
        qDebug() << "Parser2::parse()";
    }
    ~Parser2() {
       qDebug() <<"Parser2::~Parser2()";
    }
};
Q_DECLARE_METATYPE(Parser2)


void factory( const char* parserName ) {
     int id = QMetaType::type( parserName );
     if (id == -1) return; // ERROR HERE
     Parser *parser = static_cast<Parser*>(QMetaType::construct(id));
     parser->parse();
     delete parser;
}

int main () {
    qRegisterMetaType<Parser1>("Parser1");
    qRegisterMetaType<Parser2>("Parser2");

    qDebug() << "###### Trying create Parser1";
    factory("Parser1");
    qDebug() << "###### Trying create Parser2";
    factory("Parser2");
}


注册分为两步,定义的时候 使用 Q_DECLARE_METATYPE 宏。
使用的时候使用 qRegisterMetaType 函数
这样就可以在Qt 中动态地创建自己定义的类了。

但是这个方法,在注册 已有的 QWidget 和 QObject 的子类的时候会遇到问题。
原因是,在创建对象的时候,Qt 中有一个函数 qMetaTypeConstructHelper 会被调用,这个函数调用了欲创建类的复制构造函数,而复制构造函数 在 QWidget 和 QObject 的子类里是被禁用的。

解决的办法 就有两个,
一,启用 复制构造函数,
二,特化重写 qMetaTypeConstructHelper 函数。

复制构造函数 的启用 需要重新派生一个子类,就不多说了。
对于第二个方法,我们只要对于每一个特定的类 单独写一个函数就好了。如果使用宏,就会大大便捷这个过程。
代码如下。


#include <QtCore>

#define Q_DECLARE_QTBUILTIN_METATYPE(TYPE)          \
template <>                                         \
void *qMetaTypeConstructHelper(const TYPE *t) {     \
    Q_UNUSED(t)                                     \
    return new TYPE();                              \
}                                                   \
Q_DECLARE_METATYPE(TYPE)



Q_DECLARE_QTBUILTIN_METATYPE(QSettings)
Q_DECLARE_QTBUILTIN_METATYPE(QTimer)


void outputObject(const char* parseName) {
    QObject* obj = static_cast<QObject*>(QMetaType::construct(QMetaType::type(parseName)));
    qDebug() << obj;
    delete obj;
}

int main(int argc, char *argv[])
{
    qRegisterMetaType<QTimer>("QTimer");
    qRegisterMetaType<QSettings>("QSettings");

    outputObject("QTimer");
    outputObject("QSettings");

    QTimer* tmr = static_cast<QTimer*>(QMetaType::construct(QMetaType::type("QTimer")));
    tmr->setInterval(50000);
    qDebug() << tmr->interval();
    delete tmr;
    return 0;
}


引用 4 楼 dext 的回复:
dext版大,那个帖子我详细看了,我想问下,如果是QObject或者QWidget的子类的话,我一定要把所有的子类都重新派生后注册一遍,有没有其他的一些办法

#9


引用 8 楼 dext 的回复:
既然是这样,我就索性把这个问题,全部的回答一下。

对于Qt 来说,是可以做到运行时,根据对象的类名字(字符串)来获得对象的实例的,这点和一些语言的反射机制是一样的。

但是在Qt中,我们需要所额外的一步,就是注册。
只要做到了注册,我们就可以 *的创建对象了。


#include <QtCore>

class Parser {
public:
    virtual void parse() = 0;
    virtual ~Parser() {}
};

class Parser1 : public Parser {
public:
    Parser1() {
        qDebug() <<"Parser1::Parser1()";
    }
    void parse() {
        qDebug() << "Parser1::parse()";
    }
    ~Parser1() {
       qDebug() <<"Parser1::~Parser1()";
    }
};
Q_DECLARE_METATYPE(Parser1)

class Parser2 : public Parser {
public:
    Parser2() {
        qDebug() <<"Parser2::Parser2()";
    }
    void parse() {
        qDebug() << "Parser2::parse()";
    }
    ~Parser2() {
       qDebug() <<"Parser2::~Parser2()";
    }
};
Q_DECLARE_METATYPE(Parser2)


void factory( const char* parserName ) {
     int id = QMetaType::type( parserName );
     if (id == -1) return; // ERROR HERE
     Parser *parser = static_cast<Parser*>(QMetaType::construct(id));
     parser->parse();
     delete parser;
}

int main () {
    qRegisterMetaType<Parser1>("Parser1");
    qRegisterMetaType<Parser2>("Parser2");

    qDebug() << "###### Trying create Parser1";
    factory("Parser1");
    qDebug() << "###### Trying create Parser2";
    factory("Parser2");
}


注册分为两步,定义的时候 使用 Q_DECLARE_METATYPE 宏。
使用的时候使用 qRegisterMetaType 函数
这样就可以在Qt 中动态地创建自己定义的类了。

但是这个方法,在注册 已有的 QWidget 和 QObject 的子类的时候会遇到问题。
原因是,在创建对象的时候,Qt 中有一个函数 qMetaTypeConstructHelper 会被调用,这个函数调用了欲创建类的复制构造函数,而复制构造函数 在 QWidget 和 QObject 的子类里是被禁用的。

解决的办法 就有两个,
一,启用 复制构造函数,
二,特化重写 qMetaTypeConstructHelper 函数。

复制构造函数 的启用 需要重新派生一个子类,就不多说了。
对于第二个方法,我们只要对于每一个特定的类 单独写一个函数就好了。如果使用宏,就会大大便捷这个过程。
代码如下。


#include <QtCore>

#define Q_DECLARE_QTBUILTIN_METATYPE(TYPE)          \
template <>                                         \
void *qMetaTypeConstructHelper(const TYPE *t) {     \
    Q_UNUSED(t)                                     \
    return new TYPE();                              \
}                                                   \
Q_DECLARE_METATYPE(TYPE)



Q_DECLARE_QTBUILTIN_METATYPE(QSettings)
Q_DECLARE_QTBUILTIN_METATYPE(QTimer)


void outputObject(const char* parseName) {
    QObject* obj = static_cast<QObject*>(QMetaType::construct(QMetaType::type(parseName)));
    qDebug() << obj;
    delete obj;
}

int main(int argc, char *argv[])
{
    qRegisterMetaType<QTimer>("QTimer");
    qRegisterMetaType<QSettings>("QSettings");

    outputObject("QTimer");
    outputObject("QSettings");

    QTimer* tmr = static_cast<QTimer*>(QMetaType::construct(QMetaType::type("QTimer")));
    tmr->setInterval(50000);
    qDebug() << tmr->interval();
    delete tmr;
    return 0;
}


Quote: 引用 4 楼 dext 的回复:

dext版大,那个帖子我详细看了,我想问下,如果是QObject或者QWidget的子类的话,我一定要把所有的子类都重新派生后注册一遍,有没有其他的一些办法

谢谢dext
不过我在解决问题的时候,我有发现一个问题
我的是Qt5.0.2,会出现一个这样的error: 'qMetaTypeConstructHelper' is not a template function
我查了一些资料,还是没有找到解决的办法