QT入门学习(二)---继承关系、访问控制和变量定义

一:继承关系

1.1 基本继承语法

cpp 复制代码
class 子类 : public 父类 {
    // 子类定义
};

1.public 表示继承方式(还有 protectedprivate,但 Qt 中通常用 public)。

2.子类会继承父类的所有 publicprotected 成员。

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

作用

  1. 实现多线程编程,将耗时操作(如文件处理、网络请求)放在后台线程,避免阻塞 UI。

  2. 通过信号与槽实现线程间安全通信。

常用函数
函数名 功能描述
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::ReadOnlyQIODevice::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)机制

允许特定类或函数访问另一个类的 privateprotected 成员。

语法
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!";
    } // 文件在离开作用域时自动关闭
}
相关推荐
会飞的土拨鼠呀16 分钟前
Linux 测试本机与192.168.1.130 主机161/udp端口连通性
linux·运维·udp
乐观主义现代人20 分钟前
centos 9/ubuntu 一次性的定时关机
linux·ubuntu·centos
冰橙子id40 分钟前
linux——账号和权限的管理
linux·运维·服务器
范纹杉想快点毕业3 小时前
C++抽象类与多态实战解析
java·c语言·开发语言·c++·python·qt
测试老哥3 小时前
Pytest+Selenium UI自动化测试实战实例
自动化测试·软件测试·python·selenium·测试工具·ui·pytest
CHANG_THE_WORLD3 小时前
编译 Linux openssl
linux·运维·服务器
Insist7533 小时前
linux操作系统---网络协议
linux·运维·服务器
小跌—3 小时前
Linux:理解库制作与原理
linux·服务器
Logan Lie4 小时前
Linux运维笔记:1010实验室电脑资源规范使用指南
linux·运维·笔记
行云流水剑5 小时前
【学习记录】快速上手 PyQt6:设置 Qt Designer、PyUIC 和 PyRCC 在 PyCharm中的应用
python·qt·学习·pycharm