C++ Qt常用面试题整理(不定时更新)[通俗易懂]

2022-09-12 20:30:07 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

1.基础知识

1.Qt信号槽机制的优势和不足

优点:类型安全,松散耦合。缺点:同回调函数相比,运行速度较慢。

2.static和const的使用

1.static:静态变量声明,分为局部静态变量,全局静态变量,类静态成员变量。也可修饰类成员函数。

局部静态变量:存储在静态存储区,程序运行期间只被初始化一次,作用域仍然为局部作用域,在变量定义的函数或语句块中有效,程序结束时由操作系统回收资源。

全局静态变量:存储在静态存储区,静态存储区中的资源在程序运行期间会一直存在,直到程序结束由系统回收。未初始化的变量会默认为0,作用域在声明他的文件中有效。

类静态成员变量:被类的所有对象共享,包括子对象。必须在类外初始化,不可以在构造函数内进行初始化。

类静态成员函数:所有对象共享该函数,不含this指针,不可使用类中非静态成员。

2.const:常量声明,类常成员函数声明。

const和static不可同时修饰类成员函数,const修饰成员函数表示不能修改对象的状态,static修饰成员函数表示该函数属于类,不属于对象,二者相互矛盾。const修饰变量时表示变量不可修改,修饰成员函数表示不可修改任意成员变量。难记点(我是感觉很SB,除了面试,实际工作中完全没遇到):

const char* p = new char(‘a’): 表示p指向的内容不可修改但是p可修改。*p = ‘b’; // 错误 p = p2;// 正确指针可修改

char* const p = new char(‘a’): 表示p不可修改,但是p指向的内容可修改。*p= ‘b’;// 正确 p = p2;//错误

3.指针和引用的异同

指针:是一个变量,但是这个变量存储的是另一个变量的地址,我们可以通过访问这个地址来修改变量。

引用:是一个别名,还是变量本身。对引用进行的任何操作就是对变量本身进行的操作。

相同点:二者都可以对变量进行修改。

不同点:指针可以不必须初始化,引用必须初始化。指针可以有多级,但是引用只有一级(int&& a不合法, int** p合法)。指针在初始化后可以改变,引用不能进行改变,即无法再对另一个同类型对象进行引用。sizeof指针可以得到指针本身大小,sizeof引用得到的是变量本身大小。指针传参还是值传递,引用传参传的是变量本身。

4.如何理解多态

定义:同一操作作用于不同的对象,产生不同的执行结果。C 多态意味着当调用虚成员函数时,会根据调用类型对象的实际类型执行不同的操作。

实现:通过虚函数实现,用virtual声明的成员函数就是虚函数,允许子类重写。声明基类的指针或者引用指向不同的子类对象,调用相应的虚函数,可以根据指针或引用指向的子类的不同从而执行不同的操作。

Overload(重载):函数名相同,参数类型或顺序不同的函数构成重载。

Override(重写):派生类覆盖基类用virtual声明的成员函数。

Overwrite(隐藏):派生类的函数屏蔽了与其同名的基类函数。派生类的函数与基类函数同名,但是参数不同,隐藏基类函数。如果参数相同,但是基类没有virtual关键字,基类函数将被隐藏。

5.虚函数表

多态是由虚函数实现的,而虚函数主要是通过虚函数表实现的。如果一个类中包含虚函数,那么这个类就会包含一张虚函数表,虚函数表存储的每一项是一个虚函数的地址。该类的每个对象都会包含一个虚指针(虚指针存在于对象实例地址的最前面,保证虚函数表有最高的性能),需指针指向虚函数表。注意:对象不包含虚函数表,只有需指针,类才包含虚函数表,派生类会生成一个兼容基类的虚函数表。

6.常用数据结构

1.vector:向量,连续存储,可随机访问。

2.deque:双向队列,连续存储,随机访问。

3.list:链表,内存不连续,不支持随机访问。

4.stack:栈,不可随机访问,只允许再开头增加/删除元素。

5.queue:单向队列,尾部增加,开头删除。

6.set:集合,采用红黑树实现,可随机访问。查找、插入、删除时间复杂度为O(logn)。

7.map:图,采用红黑树实现,可随机访问。查找、插入、删除时间复杂度为O(logn)。

8.hash_set:哈希表,随机访问。查找、插入、删除时间复杂读为O(1)。

7.Tcp

1.三次握手:建立一个TCP连接时,需要客户端服务端总共发送三个包以确认连接的建立。在这一过程中由客户端执行connect来触发,流程如下:

2.四次挥手:断开一个Tcp连接时,需要客户端和服务端总共发送四个包以确认连接的端口。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,流程如下:

2.Qt多线程同步的几种实现方式

(1)互斥量:QMutex

QMutex类提供的是线程之间的访问顺序化。QMutex的目的是保护一个对象/数据结构或者代码段在同一时间只有一个线程可以访问。基本使用方法如下:

代码语言:javascript复制
QMutex mutex;
int var;

void function()
{
    mutex.lock();
    // 访问var
    var * var;
    mutex.unlock();
}

如果使用mutex加锁,却没有使用unlock解锁,那么就会造成死锁,其他线程永远也得不到访问变量的机会,所以为了解决这个问题,Qt引入了QMutexLocker类,二者直接可以配合使用更加方便简洁,示例如下:

代码语言:javascript复制
QMutex mutex;
int var;

void function()
{
    QMutextLocker locker(&mutex); 
    // 访问var
    var * var;
}

(2)QReadWriteLock

QMutex只允许某个时刻有一个线程对共享资源进行访问,如果需要多个线程对共享资源进行读访问,同时只有一个线程进行写访问,这种情况下就可以使用QReadWriteLock。QReadWriteLock主要实现多个线程读资源,一个线程写。写线程执行的时候会阻塞所有的读线程,而读线程之间的运行不需要进行同步。使用示例如下:

代码语言:javascript复制
int var;
QReadWriteLock lock;

void function()
{
    lock.lockForRead();
    int x = var;
    lock.unlock();
}

void function2()
{
    lock.lockForWrite();
    var = 100;
    lock.unlock();
}

和QMutexLocker一样,Qt同样提供了QReadLocker和QWriteLocker。

代码语言:javascript复制
int var;
QReadWriteLock lock;

void fun()
{
    QReadLocker(&lock);
    int x = var;
}

void fun2()
{
    QWriteLocker(&lock);
    var = 1000;
}

(3)QSemaphore

QSemaphore是QMutex的一般化,它可以保护一定数量的相同资源,而QMutex只能保护一个资源。信号量比互斥量具有更好的并发性,我们可以利用信号量实现生产者-消费者模式,如下所示:

代码语言:javascript复制
const int dataSize = 100000;
const int bufferSize = 1024;
char buffer[bufferSize];
QSemaphore freeBytes(bufferSize);
QSemaphore usedButes;

void Producer::run()
{
    for (int i = 0; i < dataSize;   i)
    {
        freeBytes.acquire();
        buffer[i % bufferSize] = i;
        usedBytes.release();
    }
}

void Consumer::run()
{
    for (int i = 0; i < dataSize;   i)
    {
        usedBytes.acquire();
        qDebug() << buffer[i % bufferSize];
        freeBytes.release();
    }
}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152875.html原文链接:https://javaforall.cn

0 人点赞