当前位置:   article > 正文

Qt/C++ 面试题整理_qt元对象系统面试题

qt元对象系统面试题

1. 什么时候必须重写拷贝构造函数

当构造函数涉及到动态存储分配空间时,要⾃⼰写拷贝构造函数,并且要深拷贝

2. 哪几种情况必须用到初始化成员列表

1)该类的成员变量是个引用;
2)该类的成员变量是const类型;
3)该类是继承一个基类,并且基类中有构造函数,构造函数里有参数;
4)该类的成员变量类型是类类型,而该类的构造函数带参数时

3. 不允许重载的5个运算符是哪些

1).* 成员指针访问运算符号
2):: 域运算符
3)Sizeof 长度运算符号
4)?: 条件运算符号
5). 成员访问符

4. 如何定义和实现一个类的成员函数为回调函数

所谓的回调函数,就是预先在系统中对函数进⾏注册,让系统知道这个函数的存在,以后,当某个事件发⽣时,再调⽤这个函数对事件进⾏响应。定义⼀个类的成员函数时在该函数前加 CALLBACK 即将其定义为回调函数,函数的实现和普通成员函数没有区别
参考:C++如何使用类的成员函数作为回调函数

5. main 函数执行以前,还会执行什么代码

全局对象的构造函数会在 main 函数之前执⾏

6. private,protect,public 三种访问限制类型的区别

private 是私有类型,只有本类中的成员函数和友元函数访问;
protect 是保护型的,本类和继承类可以访问;
public 是公有类型,任何类都可以访问;

7. 引用与指针有什么区别

1)引⽤必须被初始化,指针不必;
2)引⽤初始化以后不能被改变,指针可以改变所指的对象;
3)不存在指向空值的引⽤,但是存在指向空值的指针;

8. static 关键字

一、面向过程设计中的static
1)静态全局变量(全局分配内存、自动初始化为0、外部不可见)
2)静态局部变量(全局分配内存、执行到时初始化,无显示初始化则自动初始化为0、始终存在于静态存储区直到程序结束,但作用域为局部作用域)
3)静态函数(只能在声明它的文件中可见,不能被其它文件使用,其它文件中可以定义相同名字的函数,不会发生冲突)
二、面向对象的static关键字(类中的static关键字)
1)静态数据成员 (静态数据成员只分配一次内存,供所有对象共用、初始化需要加上类名)
2)静态成员函数 (由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长)
C++中static变量的初始化

9. const 修饰符

const 修饰变量示例说明
const 变量const int a;不能修改值,必须初始化
const 类对象const MyClass a;不能修改成员变量的值,不能调用非 const 函数
指向 const 变量的指针const int * a;指向可变,指向内容不可变
const 指针int * const a;指向不可变,指向内容可变
指向 const 变量的 const 指针const int * const a;指向内容不可变,指向也不可变
const 变量作为函数参数void myfun(const int a);函数内部不能改变此参数
const 返回值const string& myfun(void);用于返回const引用,上层不能使用返回的引用,修改对象
const 成员变量const int a; static const int a;必须在初始化列表初始化,之后不能改变。 static const 成员变量需要单独定义和初始化
const 成员函数void myfun(void) const;this指针为指向const对象的const指针,不能修改非 mutable 的成员变量

10. 定义一个宏,求最小值

#define MIN(A,B) ((A)<(B) ? (A):(B))

11. 编译的过程

1)预处理阶段(包含头文件、去注释、宏展开)
2)编译(源代码转变成汇编语言代码)
3)汇编(汇编语言代码翻译成目标机器指令,生成目标文件)
4)链接(链接库)

12. sizeof 和 strlen 的区别

1)sizeof是运算符,获得保证能容纳实现所建立的最大对象的字节大小,编译时计算完成;strlen是函数,返回字符串的长度,在运行时才能计算;
2)sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以"\0"结尾的;
3)sizeof还可以用函数做参数,比如: short f(); printf(“%d\n”, sizeof(f())); //输出的结果是sizeof(short),即2;
4)数组做sizeof的参数不退化,传递给strlen就退化为指针了;
5)sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数;

13. 类型强转

  1. static_cast(任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_const)
  2. const_cast(不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用)
  3. reinterpret_cast(改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型)
  4. dynamic_cast
    (1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查
    (2)不能用于内置的基本数据类型的强制转换
    (3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL
    (4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过(类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义)
    (5)在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全 向上转换,即为子类指针指向父类指针(一般不会出问题);向下转换,即将父类指针转化子类指针 向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败 在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试

14. int (*ptr[10])(int) 代表什么意思

定义了一个有10个元素的指针数组,且数组元素为有整形参数并返回int型的函数指针

15. 以下程序运行后会有什么结果

#include <iostream> 

void GetMemory(char *p, int num) 
{
    p = (char *)malloc( sizeof(char)*num );
} 

int main() 
{
    char *str = NULL;
    GetMemory(str, 100);
    strcpy(str, "hello"); 
    return 0;
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

程序崩溃,因为调用完GetMemory后,申请的动态内存地址并不能传递给str,main函数里面的str一直是NULL

16. 以下程序运行后会有什么结果

#include <iostream>
using namespace std; 

char *GetMemory(void) 
{
    char p[] = "hello";    
    return p;
} 

int main() 
{
    char *str = NULL;
    str = GetMemory();
    cout << str; 
    return 0;
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

很可能出现乱码,因为GetMemory返回的指针指向的是已经被释放的栈空间的地址。此地址上原来的“hello”内容很可能已经被新的内容替换

17. 以下程序的输出结果是什么

#include <iostream>
using namespace std; 

class A {
public:
    virtual void print(void) { cout << "A::print()" << endl; }
}; 

class B: public A {
public:
    virtual void print(void) { cout<<"B::print()"<<endl; }
}; 

class C: public A {
public:
    void print(void) { cout<<"C::print()"<<endl; }
}; 

void print(A a) {a.print();} 

int main() {
    A a, *pa, *pb, *pc;
    B b;
    C c; 
    pa = &a;
    pb = &b;
    pc = &c; 
    a.print(); 	// A::print()
    b.print(); 	// B::print()
    c.print(); 	// C::print() 
    pa->print(); // A::print()
    pb->print(); // B::print()
    pc->print(); // C::print() 
    print(a); 	// A::print()
    print(b); 	// A::print()
    print(c); 	// A::print() 
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

18. 求下面函数的返回值(微软)

int func(x)
{
    int countx = 0;
    while(x)
    {
        countx ++;
        x = x & (x-1);
    }
    return countx;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

假定 x = 9999。答案:8
思路:将x转化为2进制,看含有的1的个数

19. 试写出程序结果

int a = 4;
int & f(int  x)
{
    a = a + x;
    return  a;
}

int main()
{
    int t = 5;
    cout << f(t) << endl;  	// a = 9
    f(t) = 20;           	// a = 20
    cout << f(t) << endl;  	// t = 5 a = 25
    t = f(t);            	// a = 30 t = 30
    cout << f(t) << endl;  	// t = 60
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

20. 以下程序的输出结果是什么

#include <iostream>
using namespace std;

class A {
public:
    virtual ~A() {};
    virtual int test(int a, int b = 3) {
        return a + b;
    }
};

class B: public A {
public:
    int test(int a, int b = 5) {
        return a * b;
    }
};

int main()
{
    A *a = new B();
    int num = a->test(4);	
    cout << num;	\\ 12
    delete a;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

触发多态时,调用子类的 test 函数,但是默认参数用的是父类的

21. c++11/14 新特性

参考:C++11新特性

22. 分别写出 bool、int、float、指针类型的变量 a 与“零”的比较语句

1)bool : if(!a) or if(a)
2)int : if(a == 0)
3)float : const EXPRESSION EXP = 0.000001
   if (a < EXP && a >-EXP) 
4)pointer : if(a != NULL) or if(a == NULL)
  • 1
  • 2
  • 3
  • 4
  • 5

23. 写出完整版的strcpy函数

如果编写一个标准 strcpy 函数的总分值为10,下面给出几个不同得分的答案:

// 2分
void strcpy( char *strDest, char *strSrc )
{
    while( (*strDest++ = * strSrc++) != '\0' );
}

// 4分
// 将源字符串加const,表明其为输入参数,加2分
void strcpy( char *strDest, const char *strSrc )
{
    while( (*strDest++ = * strSrc++) != '\0' );
}

// 7分
void strcpy(char *strDest, const char *strSrc)
{
    // 对源地址和目的地址加非0断言,加3分
    assert( (strDest != NULL) && (strSrc != NULL) );
    while( (*strDest++ = * strSrc++) != '\0' );
}

// 10分
// 为了实现链式操作,将目的地址返回,加3分! 
char * strcpy( char *strDest, const char *strSrc )
{
    assert( (strDest != NULL) && (strSrc != NULL) );
    char *address = strDest;
    while( (*strDest++ = * strSrc++) != '\0' );
    return address;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

24. 编写类String的构造函数、析构函数和赋值函数

已知类String的原型为:

class String
{
public:
    String(const char *str = NULL); // 普通构造函数
    String(const String &other); // 拷贝构造函数
    ~ String(void); // 析构函数
    String & operator =(const String &other); // 赋值函数
private:
    char *m_data; // 用于保存字符串
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
// 普通构造函数
String::String(const char *str)
{
    if ( str == NULL )
    {
	m_data = new char[1];   // 得分点:对空字符串自动申请存放结束标志'\0'的空
	*m_data = '\0';         // 加分点:对 m_data 加 NULL 判断   
    } else {
	int length = strlen(str);
	m_data = new char[length+1];    // 若能加 NULL 判断则更好
	strcpy(m_data, str);
    }
}

// String的析构函数
String::~String(void)
{
    delete [] m_data;
}

// 拷贝构造函数
String::String(const String &other) // 得分点:输入参数为const型
{
    int length = strlen(other.m_data);
    m_data = new char[length+1];    // 加分点:对 m_data 加 NULL 判断
    strcpy(m_data, other.m_data);
}

// 赋值函数
String & String::operator =(const String &other) // 得分点:输入参数为const型
{
    if ( this == &other )  	// 得分点:检查自赋值	
	return *this; 
    delete [] m_data;   	// 得分点:释放原有的内存资源
    int length = strlen( other.m_data );
    m_data = new char[length+1];	// 加分点:对 m_data 加 NULL 判断
    strcpy( m_data, other.m_data );
    return *this;   		// 得分点:返回本对象的引用
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

25. C++的虚函数实现机制

虚函数是实现多态(动态绑定)、接口函数的基础。利用虚表实现
C++对象的内存布局,对象的前8位(64位系统)为虚表指针(vtpr),指向对象所对应的虚表
虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针
同一个类的不同实例共用同一份虚函数表,他们都通过一个虚函数表指针指向该虚函数表
参考:C++中的虚函数表实现机制以及用C语言对其进行的模拟实现
参考:虚函数和虚函数表

26. C++的智能指针相关实现

参考1:字节跳动C++/Qt PC客户端面试题精选
参考2:Modern C++ 智能指针详解

27. C++协程相关

参考:初识协程

28. 互斥锁、可重入锁、读写锁与自旋锁

1)mutex
互斥量mutex是睡眠等待类型的锁,当线程抢互斥锁失败的时候,线程会陷入休眠。优点就是节省CPU资源,缺点就是休眠唤醒会消耗一点时间
依据同一线程是否能多次加锁,把互斥量又分为如下两类:
是:递归互斥量recursive mutex,也称可重入锁,reentrant lock
否:非递归互斥量non-recursive mutex,也称不可重入锁,non-reentrant mutexread-write lock
2)读写锁
又称“共享-独占锁”,对于临界区区分读和写,读共享,写独占
读写锁的特性:
当读写锁被加了写锁时,其他线程对该锁加读锁或者写锁都会阻塞
当读写锁被加了读锁时,其他线程对该锁加写锁会阻塞,加读锁会成功
适用于多读少写的场景
3)spinlock 自旋锁
自旋,更通俗的一个词时“忙等待”(busy waiting)。最通俗的一个理解,其实就是死循环
自旋锁不会引起线程休眠。当共享资源的状态不满足时,自旋锁会不停地循环检测状态(循环检测状态利用了CPU提供的原语Compare&Exchange来保证原子性)。因为不会陷入休眠,而是忙等待的方式也就不需要条件变量。不休眠就不会引起上下文切换,但是会比较浪费CPU

29. C++类对象的内存分布

参考:C++类对象的内存分布

30. C++内存配分相关

C++程序运行时进程的内存分布情况
在这里插入图片描述
内存分为5部分,从高地址到低地址为:

  • 栈:空间向下
  • 堆:空间向上
  • 未初始化的数据段(bss):该段数据在程序开始之前由操作系统内核初始化为0,包含所有初始化为0和没有显式初始化的全局变量和静态变量
  • 初始化的数据段(data):初始化的全局变量和静态变量
  • 代码段(text):存放程序的二进制代码

C的储存区分为:

  • 栈:编译器自动分配释放
  • 堆:程序员分配释放
  • 全局区(静态区):全局变量与静态变量存放在一起,初始化与未初始化的全局变量和静态变量分别存放在两块相邻的区域。-程序结束释放
  • 常量区:程序结束释放

C++的储存区分为:

  • 栈:由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变脸、函数参数等
  • 堆:new分配的内存块,他们的释放由程序员负责。若程序员没有释放掉,程序结束后操作系统会自动回收
  • 自由存储区:malloc分配的内存块,他和堆是十分相似的,区别是用free来结束自己的声明
  • 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在C语言中,全局变量和静态变量分为初始化的和未初始化的,在C++中无区分,共同占用同一块内存区
  • 常量存储区:里面存放常量

判断规则:

  • 函数体中定义的变量通常是在栈上
  • 用malloc,new等分配内存的函数分配得到的在堆上
  • 全局变量存在全局区
  • 所有静态变量存在全局区
  • "abcd"字符串常量存放在常量区

31. C++所占内存数

参考:C++中类型、数组、结构体、类所占字节数

32. C++设计模式

参考:设计模式

33. 排序算法

参考:用C++实现十大经典排序算法

34. 如何判断一个程序是 死锁 还是 死循环

通过任务管理器中的软件状态和CPU来区分
参考:C++如何判断一个程序是 死锁 还是 死循环,如何进行问题定位与分析

35. C++内存泄漏检测

参考:C++内存泄露检查的5个方法
参考:C++ 内存泄漏检测方法

1. Qt 元对象系统

Qt 的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统
元对象系统由以下三个基础组成:
1)QObject 类,是所有使用元对象系统的类的基类。换句话说只有继承 QObject 才能使用元对象系统;
2)Q_OBJECT 宏,在一个类的 private 部分声明 ,使得类可以使用元对象的特性,如动态属性、信号与槽;
3)MOC(元对象编译器),为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。构建项目时,MOC 工具读取 C++ 源文件,当它发现类的定义里有 Q_OBJECT 宏时,它就会为这个类生成另外一个包含有元对象支持代码的 C++ 源文件,这个生成的源文件连同类的实现文件一起被编译和连接。通常这个新的C++原文件会再以前的C++原文件前面加上moc_作为新的文件名;
可参考:Qt 元对象系统

2. Qt信号槽的调用流程

1)MOC查找头文件中的signal与slots,标记出信号槽。将信号槽信息储存到类静态变量staticMetaObject中,并按照声明的顺序进行存放,建立索引;
2)connect链接,将信号槽的索引信息放到一个双向链表中,彼此配对;
3)emit被调用,调用信号函数,且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数;
4)active函数在双向链表中找到所有与信号对应的槽索引,根据槽索引找到槽函数,执行槽函数

3. Qt连接信号槽的方式(connect的第五个参数)

1)Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用 Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用 Qt::QueuedConnection 类型;
2)Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃;
3)Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个;
4)Qt::BlockingQueuedConnection:槽函数的调用时机与 Qt::QueuedConnection 一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个;
5)Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接;

4. Qt信号槽机制与优势与不足

优点:
1)类型安全。需要关联的信号槽的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的槽的参数类型和参数个数相同。若信号和槽签名不一致,编译器会报错。
2)松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可,而不需要关心是否被接受和那个对象接受了。Qt就保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。
3)灵活性。一个信号可以关联多个槽,或多个信号关联同一个槽。

不足:速度较慢。与回调函数相比,信号和槽机制运行速度比直接调用非虚函数慢10倍。 原因如下:
1)需要定位接收信号的对象;
2)安全地遍历所有关联槽;
3)编组、解组传递参数;多线程的时候,信号需要排队等待(然而,与创建对象的 new 操作及删除对象的 delete 操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的);

5. Qt中的常用容器类

1)QVector:基于数组实现的动态数组容器类,支持快速的随机访问和尾部插入操作。适合于需要频繁随机访问的情况;
2)QList:基于双向链表实现的容器类,支持高效的插入和删除操作。适合于需要频繁插入和删除元素的情况;
3)QLinkedList:基于双向链表实现的容器类,支持高效的插入和删除操作,但不支持随机访问。适合于需要频繁插入和删除元素,但不需要随机访问的情况;
4)QSet:基于哈希表实现的集合容器类,支持高效的查找和插入操作,不允许重复元素。适合于需要快速查找元素,且不需要重复元素的情况;
5)QMap:基于红黑树实现的映射容器类,支持高效的查找和插入操作,键值对按照键的大小有序排列。适合于需要按键进行排序和快速查找的情况;
6)QHash:基于哈希表实现的映射容器类,支持高效的查找和插入操作,键值对无序存储。适合于需要快速查找键值对,且不需要按键排序的情况;
7)QStringList:基于QString实现的字符串列表容器类,支持高效的字符串操作,如拼接、查找、替换等。适合于处理字符串列表的情况;
8)QByteArray:基于char数组实现的字节数组容器类,支持高效的二进制数据读写操作。适合于处理二进制数据的情况;

6. 如何进行Qt的性能优化

1)减少内存使用:使用智能指针、减少不必要的拷贝、避免频繁的 new 和 delete 操作等。如使用 QVector 代替 QList,在需要大量存储数据时能够提高性能。
2)减少绘制次数:使用 QPainter 的缓存绘制功能,对于需要频繁绘制的控件,将绘制结果缓存起来,只在需要更新时才进行重绘。
3)使用多线程:在需要大量计算的场景中,将计算放到后台线程中,避免阻塞UI线程,提高响应速度。
4)避免频繁的信号和槽连接:频繁的信号和槽连接会带来额外的开销,可以将一些信号槽的连接放到初始化阶段,避免重复连接。
5)合理使用QML:对于需要频繁更新的UI组件,使用QML实现,能够减少UI线程的工作量,提高UI性能

7. Qt样式表

参考:Qt 样式表之QSS

8. QGraphicsView 视图框架

参考:Qt之QGraphicsView入门篇

9. Qt 中的内存泄漏

参考:Qt 中的内存泄漏

10. Qt 中如何使用线程

参考:Qt 中开启线程的五种方式

11 QML 的渲染机制是怎么样的

参考:Qt Quick 渲染之 Scene Graph 详解

12 如何在QML中注册C++类

参考:Qt 中如何注册一个 C++ 类到 QML

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/57304
推荐阅读
相关标签
  

闽ICP备14008679号