C++ Qt进阶面试题
1. Qt的元对象系统(Meta-Object System)是什么?
答案:
- 定义Qt的核心特性之一提供对象间通信、运行时类型信息、动态属性系统基于标准C++扩展
- 主要功能信号和槽机制属性系统(Q_PROPERTY)对象自省(introspection)动态调用方法(QMetaObject::invokeMethod)
- 实现原理moc(Meta-Object Compiler)预处理生成额外的C++代码创建QMetaObject静态对象
- 使用示例
class MyClass : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
int value() const { return m_value; }
void setValue(int v) {
if (m_value != v) {
m_value = v;
emit valueChanged(v);
}
}
signals:
void valueChanged(int newValue);
private:
int m_value = 0;
};
// 运行时访问
MyClass obj;
obj.setProperty("value", 42);
int v = obj.property("value").toInt();
2. 信号槽的实现原理是什么?
答案:
- 底层机制基于观察者模式使用QMetaObject存储连接信息通过函数指针或索引调用槽函数
- 连接存储QObject内部维护连接列表记录信号、槽、连接类型等信息支持多对多连接
- 信号发射过程
// 信号定义(moc生成实现)
void MyClass::mySignal(int value) {
QMetaObject::activate(this, &staticMetaObject,
signalIndex, &value);
}
// activate函数遍历连接列表,调用所有槽
- 连接类型Qt::AutoConnection:自动选择(默认)Qt::DirectConnection:直接调用,同步Qt::QueuedConnection:事件队列,异步Qt::BlockingQueuedConnection:阻塞等待Qt::UniqueConnection:避免重复连接
- 性能考虑信号槽比直接函数调用慢跨线程连接有额外开销避免在性能关键路径使用
3. Qt的事件循环机制是什么?
答案:
- 事件循环QCoreApplication::exec()启动不断从事件队列取事件并分发直到quit()被调用
- 事件处理流程
事件产生 → 事件队列 → 事件循环 → 事件分发 → 事件处理
- 事件分发
bool QCoreApplication::notify(QObject* receiver, QEvent* event) {
// 1. 事件过滤器
if (receiver->eventFilter(event))
return true;
// 2. event()函数
return receiver->event(event);
}
- 自定义事件循环
QEventLoop loop; QTimer::singleShot(5000, &loop, &QEventLoop::quit); loop.exec(); // 阻塞5秒
- 事件优先级高优先级事件先处理可以使用postEvent指定优先级
4. 如何实现自定义控件?
答案:
- 方法一:组合现有控件
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget* parent = nullptr) : QWidget(parent) {
QVBoxLayout* layout = new QVBoxLayout(this);
label = new QLabel("Label");
button = new QPushButton("Button");
layout->addWidget(label);
layout->addWidget(button);
connect(button, &QPushButton::clicked,
this, &MyWidget::onButtonClicked);
}
private:
QLabel* label;
QPushButton* button;
};
- 方法二:继承并重绘
class CustomButton : public QWidget {
Q_OBJECT
public:
CustomButton(QWidget* parent = nullptr) : QWidget(parent) {
setMinimumSize(100, 40);
}
protected:
void paintEvent(QPaintEvent* event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制背景
painter.setBrush(QColor(100, 150, 200));
painter.drawRoundedRect(rect(), 5, 5);
// 绘制文本
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignCenter, "Custom");
}
void mousePressEvent(QMouseEvent* event) override {
emit clicked();
}
signals:
void clicked();
};
- 提升控件(Promote)在Qt Designer中使用将标准控件提升为自定义控件
5. Qt的模型/视图架构是什么?
答案:
- MVC架构Model:数据模型View:视图显示Controller:控制逻辑(Qt中合并到View)
- 标准模型QStandardItemModel:通用模型QStringListModel:字符串列表QFileSystemModel:文件系统QSqlTableModel:数据库表
- 标准视图QListView:列表视图QTreeView:树形视图QTableView:表格视图
- 使用示例
// 创建模型
QStandardItemModel* model = new QStandardItemModel(4, 2);
model->setHeaderData(0, Qt::Horizontal, "Name");
model->setHeaderData(1, Qt::Horizontal, "Age");
// 填充数据
model->setItem(0, 0, new QStandardItem("Alice"));
model->setItem(0, 1, new QStandardItem("25"));
// 创建视图
QTableView* view = new QTableView();
view->setModel(model);
- 自定义模型
class MyModel : public QAbstractTableModel {
Q_OBJECT
public:
int rowCount(const QModelIndex& parent) const override {
return data.size();
}
int columnCount(const QModelIndex& parent) const override {
return 2;
}
QVariant data(const QModelIndex& index, int role) const override {
if (role == Qt::DisplayRole) {
return data[index.row()][index.column()];
}
return QVariant();
}
private:
QVector<QVector<QString>> data;
};
6. Qt的国际化如何实现?
答案:
- 基本步骤标记可翻译字符串生成翻译文件翻译文本加载翻译文件
- 标记字符串
QString text = tr("Hello World");
QString text2 = QObject::tr("Welcome");
QString text3 = QCoreApplication::translate("Context", "Text");
- 生成翻译文件
# 在.pro文件中添加 TRANSLATIONS = myapp_zh_CN.ts myapp_en_US.ts # 生成.ts文件 lupdate myapp.pro # 使用Qt Linguist翻译 # 生成.qm文件 lrelease myapp.pro
- 加载翻译
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
QTranslator translator;
if (translator.load("myapp_zh_CN.qm")) {
app.installTranslator(&translator);
}
// ...
return app.exec();
}
- 动态切换语言
void changeLanguage(const QString& lang) {
static QTranslator translator;
qApp->removeTranslator(&translator);
if (translator.load(lang + ".qm")) {
qApp->installTranslator(&translator);
}
// 刷新UI
ui->retranslateUi(this);
}
7. Qt的资源系统如何使用?
答案:
- 资源文件(.qrc)
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/images">
<file>logo.png</file>
<file>icon.png</file>
</qresource>
<qresource prefix="/translations">
<file>app_zh_CN.qm</file>
</qresource>
</RCC>
- 在代码中使用
// 加载图片
QPixmap pixmap(":/images/logo.png");
QIcon icon(":/images/icon.png");
// 设置图标
button->setIcon(QIcon(":/images/icon.png"));
// 加载文件
QFile file(":/translations/app_zh_CN.qm");
if (file.open(QIODevice::ReadOnly)) {
// 读取内容
}
- 优势资源编译到可执行文件不需要单独分发资源文件跨平台路径统一
- .pro文件配置
RESOURCES += resources.qrc
8. Qt的网络编程如何实现?
答案:
- TCP通信
// 服务器
QTcpServer* server = new QTcpServer();
server->listen(QHostAddress::Any, 8080);
connect(server, &QTcpServer::newConnection, [=]() {
QTcpSocket* socket = server->nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, [=]() {
QByteArray data = socket->readAll();
qDebug() << "Received:" << data;
socket->write("Response");
});
});
// 客户端
QTcpSocket* socket = new QTcpSocket();
socket->connectToHost("127.0.0.1", 8080);
connect(socket, &QTcpSocket::connected, [=]() {
socket->write("Hello Server");
});
connect(socket, &QTcpSocket::readyRead, [=]() {
QByteArray data = socket->readAll();
qDebug() << "Received:" << data;
});
- HTTP请求
QNetworkAccessManager* manager = new QNetworkAccessManager();
QNetworkRequest request(QUrl("http://example.com/api"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* reply = manager->get(request);
connect(reply, &QNetworkReply::finished, [=]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
qDebug() << "Response:" << data;
}
reply->deleteLater();
});
- UDP通信
QUdpSocket* socket = new QUdpSocket();
socket->bind(QHostAddress::Any, 8080);
// 发送
socket->writeDatagram("Hello", QHostAddress::LocalHost, 8080);
// 接收
connect(socket, &QUdpSocket::readyRead, [=]() {
while (socket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(socket->pendingDatagramSize());
socket->readDatagram(datagram.data(), datagram.size());
qDebug() << "Received:" << datagram;
}
});
9. Qt的数据库操作如何实现?
答案:
- 连接数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("mydb.db");
if (!db.open()) {
qDebug() << "Failed to open database:" << db.lastError().text();
return;
}
- 执行SQL
// 查询
QSqlQuery query;
query.exec("SELECT * FROM users");
while (query.next()) {
int id = query.value(0).toInt();
QString name = query.value(1).toString();
qDebug() << id << name;
}
// 插入
query.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
query.addBindValue("Alice");
query.addBindValue(25);
query.exec();
// 更新
query.exec("UPDATE users SET age = 26 WHERE name = 'Alice'");
// 删除
query.exec("DELETE FROM users WHERE id = 1");
- 使用模型
QSqlTableModel* model = new QSqlTableModel();
model->setTable("users");
model->select();
QTableView* view = new QTableView();
view->setModel(model);
// 编辑
model->setData(model->index(0, 1), "Bob");
model->submitAll();
- 事务处理
db.transaction();
QSqlQuery query;
query.exec("INSERT INTO users ...");
query.exec("UPDATE accounts ...");
if (success) {
db.commit();
} else {
db.rollback();
}
10. Qt Quick和QML是什么?
答案:
- QML定义声明式UI语言类似JSON的语法用于快速开发现代UI
- 基本语法
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 640
height: 480
title: "My App"
Column {
anchors.centerIn: parent
spacing: 10
Text {
text: "Hello QML"
font.pixelSize: 24
}
Button {
text: "Click Me"
onClicked: {
console.log("Button clicked")
}
}
}
}
- C++与QML交互
// C++类
class Backend : public QObject {
Q_OBJECT
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
public:
QString message() const { return m_message; }
void setMessage(const QString& msg) {
m_message = msg;
emit messageChanged();
}
Q_INVOKABLE void doSomething() {
qDebug() << "Called from QML";
}
signals:
void messageChanged();
private:
QString m_message;
};
// 注册到QML
qmlRegisterType<Backend>("com.myapp", 1, 0, "Backend");
// QML中使用
import com.myapp 1.0
Backend {
id: backend
message: "Hello"
Component.onCompleted: {
backend.doSomething()
}
}
- 优势开发速度快UI和逻辑分离动画和过渡效果简单适合移动和嵌入式
- Qt Widgets vs Qt QuickWidgets:传统桌面应用Quick:现代UI、移动应用可以混合使用
C++面试总结 文章被收录于专栏
本专栏系统梳理C++面试高频考点,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力。
查看7道真题和解析