C++ Qt基础面试题
1. 什么是Qt?Qt有哪些特点?
答案:
- Qt定义跨平台C++图形用户界面应用程序框架由Qt Company开发和维护支持Windows、Linux、macOS、Android、iOS等
- 主要特点跨平台:一次编写,到处编译面向对象:基于C++,完全面向对象丰富的API:GUI、网络、数据库、XML等信号槽机制:独特的对象通信方式国际化支持:多语言界面开源和商业双授权:LGPL和商业许可
- Qt模块Qt Core:核心非GUI功能Qt GUI:GUI组件基础Qt Widgets:桌面UI组件Qt Network:网络编程Qt SQL:数据库集成Qt Multimedia:多媒体Qt Quick:QML声明式UI
- 应用场景桌面应用程序嵌入式系统移动应用工业控制
C++面试题合集 : https://www.nowcoder.com/creation/manager/columnDetail/MJ4oG8
2. 什么是信号和槽?如何使用?
答案:
- 定义Qt的核心机制,用于对象间通信信号(Signal):事件发生时发出槽(Slot):响应信号的函数松耦合,类型安全
- 基本使用
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget() {
QPushButton* button = new QPushButton("Click", this);
// 连接信号和槽
connect(button, &QPushButton::clicked,
this, &MyWidget::onButtonClicked);
}
private slots:
void onButtonClicked() {
qDebug() << "Button clicked!";
}
};
- 连接方式
// 1. 传统方式(Qt4)
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
// 2. 函数指针方式(Qt5,推荐)
connect(sender, &Sender::signal, receiver, &Receiver::slot);
// 3. Lambda表达式
connect(button, &QPushButton::clicked, [=]() {
qDebug() << "Clicked!";
});
- 特点一个信号可以连接多个槽多个信号可以连接同一个槽信号可以连接信号连接可以是跨线程的类型安全(Qt5函数指针方式)
3. Q_OBJECT宏的作用是什么?
答案:
- 作用启用Qt的元对象系统必须放在类声明的开头使类支持信号槽机制
- 提供的功能信号和槽属性系统(Q_PROPERTY)对象树和父子关系运行时类型信息(RTTI)动态属性对象名称
- 使用示例
class MyClass : public QObject {
Q_OBJECT // 必须在最前面
public:
MyClass(QObject* parent = nullptr);
signals:
void mySignal();
private slots:
void mySlot();
};
- 注意事项必须继承自QObject或其子类类定义必须在.h文件中需要运行moc(元对象编译器)不能用于模板类
4. Qt的对象树和内存管理是什么?
答案:
- 对象树机制父对象管理子对象的生命周期父对象销毁时自动销毁所有子对象避免内存泄漏
- 使用示例
QWidget* parent = new QWidget();
QPushButton* button = new QPushButton("Click", parent); // 指定父对象
QLabel* label = new QLabel("Text", parent);
// 只需要删除父对象
delete parent; // button和label自动删除
- 内存管理规则有父对象的QObject不需要手动delete没有父对象的需要手动管理栈对象自动管理使用智能指针管理无父对象的堆对象
- 最佳实践
// 好:指定父对象
QLabel* label = new QLabel("Text", this);
// 好:栈对象
void func() {
QLabel label("Text");
}
// 需要注意:无父对象
QLabel* label = new QLabel("Text");
// 需要手动delete或使用智能指针
5. Qt中的事件机制是什么?
答案:
- 事件系统Qt使用事件驱动模型事件由QEvent类及其子类表示事件循环处理事件队列
- 事件类型鼠标事件:QMouseEvent键盘事件:QKeyEvent绘制事件:QPaintEvent定时器事件:QTimerEvent自定义事件:继承QEvent
- 事件处理方式
class MyWidget : public QWidget {
protected:
// 1. 重写事件处理函数
void mousePressEvent(QMouseEvent* event) override {
if (event->button() == Qt::LeftButton) {
qDebug() << "Left button pressed";
}
QWidget::mousePressEvent(event); // 调用基类
}
// 2. 重写event()函数
bool event(QEvent* event) override {
if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
// 处理按键
return true; // 事件已处理
}
return QWidget::event(event);
}
// 3. 安装事件过滤器
bool eventFilter(QObject* obj, QEvent* event) override {
if (obj == targetWidget && event->type() == QEvent::MouseMove) {
// 处理事件
return true; // 过滤掉事件
}
return QWidget::eventFilter(obj, event);
}
};
- 事件传递事件从子控件向父控件传递可以通过accept()或ignore()控制传递事件过滤器可以拦截事件
6. QWidget、QMainWindow、QDialog的区别?
答案:
- QWidget所有UI对象的基类最基本的窗口组件可以作为独立窗口或其他控件的容器
QWidget* widget = new QWidget();
widget->setWindowTitle("My Widget");
widget->show();
- QMainWindow主窗口类,继承自QWidget提供菜单栏、工具栏、状态栏、停靠窗口适合复杂的应用程序主窗口
QMainWindow* mainWindow = new QMainWindow(); mainWindow->setMenuBar(new QMenuBar()); mainWindow->setStatusBar(new QStatusBar()); mainWindow->setCentralWidget(new QWidget());
- QDialog对话框类,继承自QWidget模态或非模态对话框提供exec()方法阻塞执行
QDialog dialog;
dialog.setWindowTitle("My Dialog");
if (dialog.exec() == QDialog::Accepted) {
// 用户点击了确定
}
- 选择建议简单窗口:QWidget应用主窗口:QMainWindow对话框:QDialog
7. Qt中的布局管理器有哪些?
答案:
- 主要布局管理器QHBoxLayout:水平布局QVBoxLayout:垂直布局QGridLayout:网格布局QFormLayout:表单布局QStackedLayout:堆叠布局
- 使用示例
// 垂直布局
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->addWidget(new QPushButton("Button 1"));
vLayout->addWidget(new QPushButton("Button 2"));
vLayout->addWidget(new QPushButton("Button 3"));
widget->setLayout(vLayout);
// 网格布局
QGridLayout* gridLayout = new QGridLayout();
gridLayout->addWidget(new QLabel("Name:"), 0, 0);
gridLayout->addWidget(new QLineEdit(), 0, 1);
gridLayout->addWidget(new QLabel("Age:"), 1, 0);
gridLayout->addWidget(new QSpinBox(), 1, 1);
// 嵌套布局
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->addLayout(vLayout);
hLayout->addWidget(new QTextEdit());
- 布局属性间距:setSpacing()边距:setContentsMargins()拉伸因子:addStretch()、setStretch()对齐:setAlignment()
- 优势自动调整控件大小和位置响应窗口大小变化跨平台一致性
8. QString和std::string的区别?
答案:
- QString特点Qt的字符串类使用UTF-16编码隐式共享(写时复制)丰富的API
- 主要区别
// QString
QString str = "Hello";
str.append(" World");
str.toUpper();
int len = str.length();
// std::string
std::string str = "Hello";
str.append(" World");
// 需要算法库转大写
int len = str.length();
- 转换
// QString -> std::string QString qstr = "Hello"; std::string stdstr = qstr.toStdString(); // std::string -> QString std::string stdstr = "Hello"; QString qstr = QString::fromStdString(stdstr); // QString -> const char* const char* cstr = qstr.toUtf8().constData(); // const char* -> QString QString qstr = QString::fromUtf8(cstr);
- 性能QString的隐式共享减少拷贝QString的Unicode支持更好在Qt项目中优先使用QString
9. Qt中的容器类有哪些?
答案:
- 顺序容器QList:类似std::vector,最常用QVector:连续内存,随机访问快QLinkedList:双向链表QStack:栈QQueue:队列
- 关联容器QMap:有序映射,红黑树QHash:哈希表,无序QSet:集合,基于QHashQMultiMap:允许重复键QMultiHash:允许重复键的哈希表
- 使用示例
// QList
QList<int> list;
list << 1 << 2 << 3;
list.append(4);
// QMap
QMap<QString, int> map;
map["one"] = 1;
map["two"] = 2;
map.insert("three", 3);
// QHash
QHash<QString, QString> hash;
hash["key1"] = "value1";
hash["key2"] = "value2";
// 遍历
foreach (const QString& key, map.keys()) {
qDebug() << key << map[key];
}
- 与STL对比Qt容器隐式共享,减少拷贝Qt容器API更简洁STL容器性能可能更好可以混用,但建议统一
10. 如何实现多线程?QThread的使用?
答案:
- 方法一:继承QThread
class WorkerThread : public QThread {
Q_OBJECT
protected:
void run() override {
// 在新线程中执行
for (int i = 0; i < 10; i++) {
emit progress(i);
sleep(1);
}
emit finished();
}
signals:
void progress(int value);
void finished();
};
// 使用
WorkerThread* thread = new WorkerThread();
connect(thread, &WorkerThread::finished, thread, &QObject::deleteLater);
thread->start();
- 方法二:moveToThread(推荐)
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
// 工作代码
emit resultReady("Result");
}
signals:
void resultReady(const QString& result);
};
// 使用
QThread* thread = new QThread();
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &MyClass::handleResult);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
- 方法三:QtConcurrent
#include <QtConcurrent>
// 异步执行函数
QFuture<int> future = QtConcurrent::run([]() {
// 耗时操作
return 42;
});
// 获取结果
int result = future.result();
// 映射操作
QList<int> list = {1, 2, 3, 4, 5};
QFuture<void> future = QtConcurrent::map(list, [](int& x) {
x *= 2;
});
- 线程安全使用QMutex保护共享数据使用信号槽跨线程通信(自动排队)避免在非主线程操作UI
- 注意事项QObject有线程亲和性UI操作必须在主线程正确管理线程生命周期
C++面试总结 文章被收录于专栏
本专栏系统梳理C++面试高频考点,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力。
