一:继承关系
1.1 基本继承语法
cpp
class 子类 : public 父类 {
// 子类定义
};
1.
public 表示继承方式(还有 protected
和 private
,但 Qt 中通常用 public
)。
2.子类会继承父类的所有 public
和 protected
成员。
3.子类可以重写(override)父类的虚函数。
1.2 QT的典型继承链
这里我只讲OBJECT继承链
cpp
QObject
├── QWidget
│ ├── QAbstractButton
│ │ ├── QPushButton
│ │ ├── QCheckBox
│ │ └── QRadioButton
│ ├── QLineEdit
│ ├── QLabel
│ ├── QComboBox
│ ├── QGroupBox
│ └── QFrame
│ ├── QSplitter
│ ├── QScrollArea
│ └── QToolBox
├── QThread
├── QTimer
├── QAbstractItemModel
└── QIODevice
1.2.1 QObject
QObject是所有对象的基类,作为根节点。
QWidget、QThread、QTimer 等均继承自 QObject
核心机制:
对象树 :通过 parent
参数自动管理内存(如 QWidget(parent)
)。
信号与槽 :所有子类均可使用 connect()
实现事件通信。
示例代码
cpp
QWidget *widget = new QWidget(parent); // parent为QObject指针
// widget的生命周期由parent管理
1.2.2 QWidget 与 UI 组件
GUI 组件的抽象:QAbstractButton → QPushButton/QCheckBox/QRadioButton
QLineEdit、QLabel、QComboBox 等直接继承 QWidget。
布局系统 :通过 QLayout
管理子控件位置(如 QVBoxLayout::addWidget()
)。
示例代码
cpp
QPushButton *button = new QPushButton("Click me", this); // this为QWidget*
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(button); // 布局管理button的位置
1.2.3 线程模块QThread
作用
-
实现多线程编程,将耗时操作(如文件处理、网络请求)放在后台线程,避免阻塞 UI。
-
通过信号与槽实现线程间安全通信。
常用函数
函数名 | 功能描述 |
---|---|
start() |
启动线程,触发 run() 方法执行。 |
run() |
线程入口点,需在子类中重写(继承 QThread 方式)。 |
quit() |
请求线程退出(需线程内部配合检查)。 |
terminate() |
强制终止线程(不推荐,可能导致资源泄漏)。 |
wait() |
阻塞当前线程,直到目标线程结束。 |
isRunning() |
判断线程是否正在运行。 |
示例代码
cpp
// 方式1:继承QThread(简单但有限制)
class WorkerThread : public QThread {
Q_OBJECT
protected:
void run() override {
for (int i = 0; i < 100; ++i) {
emit progress(i);
msleep(100); // 模拟耗时操作
}
}
signals:
void progress(int value);
};
// 使用
WorkerThread *thread = new WorkerThread(this);
connect(thread, &WorkerThread::progress, ui->progressBar, &QProgressBar::setValue);
thread->start(); // 启动线程
1.2.4 定时器模块QTimer
1.实现周期性任务
2.基于事件循环,不会阻塞线程。
常用函数
函数名 | 功能描述 |
---|---|
start(int msec) |
启动定时器,每隔 msec 毫秒触发一次 timeout 信号。 |
stop() |
停止定时器。 |
setSingleShot(bool) |
设置是否单次触发(true 表示仅触发一次)。 |
isActive() |
判断定时器是否正在运行。 |
interval() |
返回当前定时器的间隔时间(毫秒)。 |
示例代码
cpp
// 周期性定时器
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MyClass::updateStatus);
timer->start(1000); // 每秒触发一次timeout信号
// 单次延迟执行
QTimer::singleShot(3000, this, [this]() {
qDebug() << "3秒后执行此代码";
});
1.2.5 数据模型QAbstractItemModel
1.实现模型 - 视图 - 控制器(MVC)架构,将数据与显示分离。
2.支持多种视图(列表、表格、树)展示同一数据。
常用函数(需重写)
函数名 | 功能描述 |
---|---|
rowCount() |
返回模型的行数。 |
columnCount() |
返回模型的列数。 |
data(const QModelIndex &index, int role) |
返回指定索引和角色的数据(如 Qt::DisplayRole 表示显示文本)。 |
headerData() |
返回表头数据。 |
setData() |
修改模型数据(需配合 dataChanged 信号)。 |
示例代码
cpp
// 自定义表格模型
class PersonModel : public QAbstractTableModel {
Q_OBJECT
public:
enum Columns { Name, Age, ColumnCount };
int rowCount(const QModelIndex &) const override { return m_data.size(); }
int columnCount(const QModelIndex &) const override { return ColumnCount; }
QVariant data(const QModelIndex &index, int role) const override {
if (role == Qt::DisplayRole) {
const auto &person = m_data[index.row()];
return (index.column() == Name) ? person.name : QString::number(person.age);
}
return QVariant();
}
void addPerson(const QString &name, int age) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_data.append({name, age});
endInsertRows();
}
private:
struct Person { QString name; int age; };
QList<Person> m_data;
};
// 使用
PersonModel model;
model.addPerson("Alice", 25);
QTableView *view = new QTableView();
view->setModel(&model); // 视图自动更新
1.2.5 I/O 操作QIODevice
1.提供统一的输入输出接口,支持文件、网络、内存等不同设备。
2.支持同步和异步操作。
常用函数
函数名 | 功能描述 |
---|---|
open(OpenMode mode) |
打开设备(如 QIODevice::ReadOnly 、QIODevice::WriteOnly )。 |
close() |
关闭设备。 |
read(qint64 maxSize) |
读取最多 maxSize 字节的数据。 |
readAll() |
读取所有可用数据。 |
write(const QByteArray &data) |
写入数据。 |
atEnd() |
判断是否到达数据末尾。 |
bytesAvailable() |
返回可用字节数。 |
示例代码
cpp
// 文件读写
QFile file("data.txt");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << "Hello, World!\n";
file.close();
}
// 网络通信(TCP客户端)
QTcpSocket socket;
socket.connectToHost("127.0.0.1", 1234);
connect(&socket, &QTcpSocket::readyRead, [&]() {
qDebug() << "Received:" << socket.readAll();
});
socket.write("Request data");
二:访问控制
2.1 C++ 基础访问控制符
Qt 基于 C++,因此首先需要理解 C++ 的三种基本访问控制符:
控制符 | 含义 |
---|---|
public |
所有代码均可访问(包括类外部、子类)。 |
protected |
仅类内部和子类可访问,类外部不可访问。 |
private |
仅类内部可访问,子类和类外部均不可访问。 |
示例代码:
cpp
class Base {
public:
int publicVar; // 公共变量,可被任何代码访问
protected:
int protectedVar; // 受保护变量,仅Base及其子类可访问
private:
int privateVar; // 私有变量,仅Base内部可访问
};
class Derived : public Base {
public:
void accessMembers() {
publicVar = 1; // 合法:public成员可被任何代码访问
protectedVar = 2; // 合法:子类可访问protected成员
// privateVar = 3; // 非法:子类无法访问父类的private成员
}
};
2.2 Qt 特有的访问控制机制
1. Q_OBJECT 宏与元对象系统
作用 :Q_OBJECT
宏使类支持信号与槽、属性系统和元对象反射。
访问限制 :所有使用 Q_OBJECT
的类必须继承自 QObject
,且必须在头文件中声明。
示例代码
cpp
class MyClass : public QObject {
Q_OBJECT // 必须包含此宏
public:
explicit MyClass(QObject *parent = nullptr);
signals:
void mySignal(); // 信号默认是protected的
public slots:
void mySlot(); // 槽函数可设置为public/protected/private
};
2. 信号的访问控制
默认权限 :信号默认是 protected
的,只能通过 emit
语句在类内部或子类中触发。
设计意图:防止外部代码直接触发信号,确保事件流的可控性。
示例代码
cpp
class Button : public QPushButton {
Q_OBJECT
public:
void fakeClick() {
// emit clicked(); // 非法:信号是protected的,不能直接调用
click(); // 合法:调用父类的public方法,该方法内部会emit clicked()
}
};
3. 属性系统的访问控制(Q_PROPERTY)
语法
cpp
Q_PROPERTY(type name READ read WRITE write NOTIFY notify)
1.READ
方法必须是 public
的。
2.WRITE
方法(如果有)必须是 public
的。
3.NOTIFY
信号必须是 protected
的(由 Qt 自动处理)。
示例代码
cpp
class Person : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
explicit Person(QObject *parent = nullptr);
QString name() const { return m_name; } // READ方法必须public
void setName(const QString &name) { // WRITE方法必须public
if (m_name != name) {
m_name = name;
emit nameChanged(name);
}
}
signals:
void nameChanged(const QString &name); // NOTIFY信号默认protected
private:
QString m_name;
};
4. 事件处理函数的访问控制
1.常见事件函数 :paintEvent()
、mousePressEvent()
等默认是 protected
的。
2.设计意图:鼓励子类重写这些函数,而不是从外部直接调用。
示例代码
cpp
class CustomWidget : public QWidget {
Q_OBJECT
protected:
void paintEvent(QPaintEvent *event) override {
// 自定义绘制逻辑
QPainter painter(this);
painter.drawText(rect(), "Custom Text");
QWidget::paintEvent(event); // 调用父类实现
}
};
2.3 继承与访问控制
1. 继承方式对访问权限的影响
继承方式 | 基类的 public 成员 |
基类的 protected 成员 |
基类的 private 成员 |
---|---|---|---|
public |
仍为 public |
仍为 protected |
不可访问 |
protected |
变为 protected |
变为 protected |
不可访问 |
private |
变为 private |
变为 private |
不可访问 |
示例代码
cpp
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub 保持 public
// prot 保持 protected
// priv 不可访问
};
class PrivateDerived : private Base {
// pub 变为 private
// prot 变为 private
// priv 不可访问
};
2. 子类对父类成员的访问规则
1.private
成员:子类永远无法直接访问。
2.protected
成员:子类可访问,但外部代码不可访问。
3.public
成员:子类和外部代码均可访问。
2.4 友元(Friend)机制
允许特定类或函数访问另一个类的 private
和 protected
成员。
语法
cpp
friend class FriendClass;
friend void friendFunction();
示例代码
cpp
class BankAccount {
private:
double balance;
public:
friend class BankManager; // BankManager可访问所有private/protected成员
friend void resetBalance(BankAccount &account); // 友元函数
};
class BankManager {
public:
void adjustBalance(BankAccount &account) {
account.balance = 0; // 合法:BankManager是友元类
}
};
void resetBalance(BankAccount &account) {
account.balance = 0; // 合法:resetBalance是友元函数
}
2.5. 访问控制的最佳实践
1.最小权限原则
尽量将成员设为 private
,仅通过 public
接口暴露必要功能。
示例代码
cpp
class Counter {
private:
int m_count = 0;
public:
int count() const { return m_count; } // 只读访问
void increment() { m_count++; } // 提供受控修改
};
2.使用 protected
进行接口扩展
当需要子类重写某些功能时,将函数设为 protected virtual
。
示例代码
cpp
class Shape {
public:
double area() const { return doCalculateArea(); }
protected:
virtual double doCalculateArea() const = 0; // 子类必须实现
};
3.避免过度使用友元
友元破坏了封装性,优先考虑通过 public
接口或继承解决问题。
三:变量定义
1. 局部变量
作用域:函数或代码块内部。
生命周期:栈上分配,离开作用域自动销毁。
示例:
cpp
void MyClass::doWork() {
int count = 0; // 基本类型
QString message = "Hello"; // Qt类
QVector<int> numbers{1, 2, 3}; // 容器类
} // 变量在此处销毁
2. 成员变量
作用域:类的所有成员函数。
生命周期:与对象相同(堆或栈)。
访问控制 :通过 public
/protected
/private
限制。
cpp
class MyClass : public QObject {
Q_OBJECT
private:
int m_counter = 0; // 前缀 `m_` 是常见约定
QString m_name; // 默认初始化
QScopedPointer<QTimer> m_timer; // 智能指针管理资源
};
3. 静态变量
作用域 :类或文件内部(取决于 static
位置)。
生命周期:程序启动时创建,程序结束时销毁。
示例代码
cpp
class MyClass {
public:
static int instanceCount; // 类静态变量,所有实例共享
};
int MyClass::instanceCount = 0; // 必须在类外初始化
// 文件静态变量(仅当前文件可见)
static QString s_appName = "MyApp";
4. Qt 特有的变量类型
1. Qt 基本数据类型
类型 | 描述 | 等价 C++ 类型 |
---|---|---|
qint8 |
8 位有符号整数 | signed char |
quint32 |
32 位无符号整数 | unsigned int |
qreal |
浮点数(通常为 double ) |
double |
QString |
Unicode 字符串 | - |
QByteArray |
二进制数据 | - |
QVariant |
可变类型(可存储多种数据类型) | - |
cpp
qint64 fileSize = 1024 * 1024; // 64位整数
QString text = "你好,Qt"; // Unicode字符串
QByteArray data = QByteArray::fromHex("48656C6C6F"); // 二进制数据
2. 容器类
优势:自动内存管理、支持隐式共享(写时复制)。
常用容器:
cpp
QVector<int> numbers; // 动态数组(连续内存)
QList<QString> names; // 链表(适合频繁插入删除)
QMap<QString, int> ages; // 键值对(红黑树实现,有序)
QHash<QString, int> scores; // 哈希表(无序,查找更快)
示例代码
cpp
QVector<QString> fruits{"Apple", "Banana"};
fruits.append("Cherry");
QMap<QString, int> person;
person["age"] = 30;
person["height"] = 175;
5. 内存管理与智能指针
1. 对象树管理(QObject 派生类)
机制 :通过 parent
参数自动管理内存。
示例代码
cpp
QWidget *window = new QWidget(nullptr); // 顶级窗口
QPushButton *button = new QPushButton("Click", window); // button的parent是window
// 当window被删除时,button会自动被删除
2. 智能指针
QScopedPointer
:独占所有权,离开作用域自动删除。
cpp
void func() {
QScopedPointer<QTimer> timer(new QTimer());
// timer在函数结束时自动释放
}
QSharedPointer
:共享所有权,引用计数为 0 时删除。
cpp
void shareData() {
QSharedPointer<QImage> image(new QImage("image.png"));
QSharedPointer<QImage> copy = image; // 共享同一对象
// 当所有sharedPointer都被销毁时,QImage才会被删除
}
6. 线程安全与变量
1. 线程局部存储(QThreadStorage)
作用:为每个线程创建独立的变量副本。
示例代码
cpp
QThreadStorage<int> threadCounter;
void incrementCounter() {
if (!threadCounter.hasLocalData())
threadCounter.setLocalData(0);
threadCounter.localData()++;
} // 每个线程的counter独立计数
2. 原子变量(QAtomicInteger)
作用:提供无锁的线程安全操作。
cpp
QAtomicInteger<int> atomicCounter(0);
void threadFunc() {
atomicCounter.fetchAndAddOrdered(1); // 原子自增
}
7. 属性系统(Q_PROPERTY)
1. 声明属性
Q_PROPERTY(type name READ read WRITE write NOTIFY notify)
2. 通过元对象系统访问属性
Person person;
person.setProperty("name", "Alice"); // 动态设置属性
QString name = person.property("name").toString(); // 动态获取属性
8. 变量初始化与资源管理
1. 成员变量初始化
构造函数初始化列表:
cpp
class MyClass {
public:
MyClass() : m_value(0), m_name("") {} // 初始化列表
private:
int m_value;
QString m_name;
};
就地初始化(C++11+):
cpp
class MyClass {
private:
int m_value = 0; // 就地初始化
QString m_name{"Name"}; // 统一初始化语法
};
2. 资源管理(RAII)
使用 QFile
自动管理文件句柄。
cpp
void writeToFile() {
QFile file("data.txt");
if (file.open(QIODevice::WriteOnly)) {
QTextStream out(&file);
out << "Hello, Qt!";
} // 文件在离开作用域时自动关闭
}