《C++ 类、对象、函数彻底搞懂》
核心结论(先看 一表 + 三条)
| 概念 | 一句话理解 |
|---|---|
class |
把变量+函数打包,比 struct 多了函数和访问控制 |
:: |
"属于哪个类",左边类名右边函数名 |
explicit |
防呆锁,禁止编译器偷偷转型 |
构造函数 Client::Client |
必须和类名一样,创建对象时自动调 |
初始化列表 : QObject(parent) |
在 {} 之前初始化父类和成员 |
this |
指向当前对象自己 |
&类名::函数名 |
取类内·成员函数地址 |
connect((指定发送者)发,(什么信号与函数绑定)信号, |
注册回调——信号来了自动调槽函数 |
signals: |
类内只需声明,Qt 自动生成实现 |
slots: |
普通函数,但能被信号触发 |
m_ 前缀 |
成员变量命名习惯,一看就知道是 member |
new QTcpSocket(this) |
创建对象(套接字) + 指定父对象(当前函数自己),Qt 自动管内存 |
-
类:类型定义(编译时的蓝图),不占运行内存。
-
对象:类的实例(变量),占内存。
-
函数:可执行代码,包括普通函数、构造/析构、成员函数、信号、槽。
-
构造函数/析构函数也是函数,不是对象。
-
explicit是修饰构造函数的关键字(防止被改为对象),关键点有讲解。
-
class 的完整使用流程(5 步)
| 步骤 | 文件/动作 | 本质 |
|---|---|---|
| 1. 声明类 | client.h 写 class Client { ... }; |
定义类型成员(变量+函数) |
| 2. 实现成员函数 | client.cpp 写 Client::connectToServer(...){...} |
填充函数体 |
| 3. 创建对象 | Client *c = new Client(this); 或 Client c; |
分配内存 + 调用构造函数 |
| 4. 调用成员函数 | c->sendCommand("scan"); |
执行代码,操作对象内部数据 |
| 5. 连接信号槽 | connect(c, &Client::tagReceived, this, &MyWidget::handleTag); |
建立对象间的松耦合通信 |
关键点:
-
new Client(this):堆分配 + 自动调用构造函数,显示声明 客户端带可选父类参数 ,用于创建对象
explicit Client(QObject *parent = nullptr);。 -
Client c;:栈分配 + 自动调用构造函数(作用域结束调析构函数 ~ 开头)。 -
构造函数可重载,
explicit禁止隐式转换(例如禁止Client c = "str";)。 -
重载:同名函数(构造)不同参数,编译器自动匹配。
多继承:C++ 允许,但 Qt 的 QObject 有限制
1. 什么是「继承」?
继承就是:让一个类(子类),直接拿到另一个类(父类)的所有功能和特性,不用自己再写一遍。
- 你创建一个
QObject父类(Qt 里所有支持信号槽的类的 “祖宗”) - 再写一个
MyDevice : public QObject - 那么
MyDevice就继承了QObject的所有功能(比如信号槽机制、父子对象内存管理),还能加自己的代码。
2. 用图里的代码讲
cpp
class MyDevice : public QObject {
Q_OBJECT
QTcpSocket *socket;
QTimer *timer;
};
这里:
MyDevice : public QObjectMyDevice是子类QObject是父类: public QObject就是继承关系
效果:
MyDevice自动拥有了QObject的信号槽、事件循环、父子对象管理等所有能力- 它自己又加了
socket和timer两个成员变量,扩展了自己的功能
3. 为什么不能同时继承两个 QObject 子类?
这就是你图里说的 “章程冲突”:
QObject是所有 Qt 信号槽类的基类,里面有一套自己的元数据(Q_OBJECT宏生成的)- 如果你写
class MyDevice : public QTcpSocket, public QTimer- 这两个父类都继承自
QObject - 就会让
MyDevice里出现两套QObject的元数据 - 编译器不知道该用哪一套,直接报错(就是所谓的 “菱形继承 / 元数据冲突”)
- 这两个父类都继承自
4. 图里的解决方案,怎么理解?
选一个当 “主公司”(继承),另一个当 “外包团队”(成员变量):
- 让
MyDevice只继承QObject(主公司,只留一套元数据) - 把
QTcpSocket和QTimer作为成员变量(外包团队,各自用自己的元数据,互不冲突)
5. 一句话记住
- 继承:就是 “我是你,我有你的所有东西”
- 一个类只能继承一个
QObject子类,否则会有两套信号槽元数据冲突 - 要多个 Qt 功能,就继承一个,其他当成员变量
C++ 原生多继承(任意多个基类)
cpp
class A { int a; };
class B { int b; };
class C : public A, public B { }; // OK
Qt 中的限制
一个类不能同时继承两个直接或间接派生自 QObject 的类。
原因:QObject 的元对象系统(moc)只支持单条继承链。多 QObject 基类会导致元数据冲突。
cpp
class X : public QObject { Q_OBJECT; };
class Y : public QObject { Q_OBJECT; };
class Z : public X, public Y { Q_OBJECT; }; // 错误!moc 无法处理
正确做法
在 Qt 里,关于继承的铁律只有一条:
一个类只能继承 1 个带
Q_OBJECT的类(也就是QObject及其子类,比如QTcpSocket、QTimer),否则信号槽的元数据会冲突报错。
但有两种完全合法的方式,拿到多个类的功能:
-
继承一个
QObject+ 多个普通 C++ 类(普通类无Q_OBJECT)cpp
class Helper { public: void calc(); }; class Worker : public QObject, public Helper { Q_OBJECT; }; // OK -
组合:如果需要两个
QObject子类的功能,用成员变量cpp
// 普通C++类:不带Q_OBJECT,只是做计算用 class Helper { public: void calc() { // 这里可以写你的业务逻辑 int result = 1 + 1; } }; // Qt类:继承QObject + 普通类Helper class Worker : public QObject, public Helper { Q_OBJECT // 只能有一个Q_OBJECT宏 public: Worker(QObject *parent = nullptr) : QObject(parent) {} };
为什么这个写法合法?
Worker只继承了一个带Q_OBJECT的QObjectHelper是普通 C++ 类,没有信号槽元数据,所以不会冲突
反面教材(千万不要这么写!)
很多新手会犯的错,直接继承两个 QObject 子类:
cpp
// 错误写法!会编译报错!
class MyDevice : public QTcpSocket, public QTimer {
Q_OBJECT // 这里会出现两套元数据,冲突!
};
代码里的重点(真正需要记住的)
-
explicit RfidClient(QObject *parent = nullptr);
→ 构造函数声明,explicit防止RfidClient c = someQObject;这种隐式构造。 -
RfidClient client;
→ 对象(栈上),自动调用构造函数。 -
client.connect();
→ 调用成员函数,操作client这个对象。 -
信号
tagReceived(QString epc, QString tid);
→ 特殊函数声明(moc生成实现),不返回,可被emit触发。 -
槽函数
onReadyRead();
→ 普通成员函数,可被信号自动调用。 -
多继承限制:如果一个类已经继承了
QObject(如RfidClient),就不能再继承另一个QObject子类。
一句话最终总结 + 两种方案
类是类型,对象是变量,函数是动作;构造析构也是函数;C++ 允许多继承,但 QObject 子类只能单继承 QObject,其余用组合。
| 方案 | 适用场景 | 核心思路 |
|---|---|---|
| 继承 1 个 QObject + 多个普通类 | 你的普通类只是工具类,不需要信号槽 | 只留一套 Qt 元数据,其他类当 “附加功能” |
| 组合(成员变量) | 需要多个 Qt 类的信号槽功能 | 只继承 1 个 QObject,其他 Qt 类作为成员变量 |
更多推荐

所有评论(0)