C++语言基础4:例程讲解(结合在QT的应用)
📅 2026/7/5 14:29:55
👁️ 阅读次数
📝 编程学习
我们从最基础的语法、每个关键字 / 符号的含义、每一行的执行逻辑逐句拆解,全程用新手能理解的表述,你可以对着代码一行一行看。
例程如下:
void TemperatureBar::update_data(int base_address, const QVector<quint16> &value) { Q_UNUSED(base_address); int offset = m_address - m_base_address; if (offset >= 0 && offset < value.size()) { quint16 raw_value = value.at(offset); float display_value = raw_value; if (m_is_shift) { display_value = raw_value / 10.0f; } set_value(display_value); } }先整体说下:这是 Qt/C++ 里一个自定义控件类TemperatureBar(温度条控件)的成员函数,功能是:接收一批寄存器原始数据,计算出当前温度条对应的数据,转换成真实温度值后,更新控件显示。
逐行逐字语法详解
第 1 行:函数定义(函数头)
void TemperatureBar::update_data(int base_address, const QVector<quint16> &value)这是整个函数的声明 + 定义开头,我们拆成每一部分讲:
void函数的返回值类型。void表示 “空”,意思是这个函数执行完之后,不会返回任何结果给调用者,只负责执行内部的逻辑。TemperatureBar::update_dataupdate_data是函数名。::叫作用域解析符,它的作用是明确说明:update_data不是普通的全局函数,而是TemperatureBar这个类里面的成员函数。- 补充:
TemperatureBar是一个自定义的类(温度条控件类),这个函数是它的功能之一。
(int base_address, const QVector<quint16> &value)括号里是函数的参数列表,也就是调用这个函数时需要传进来的数据,这里有 2 个参数:第一个参数:
int base_addressint:参数类型,整数型。base_address:参数名,意思是 “基地址”,一般代表批量读取寄存器时的起始地址。
第二个参数:
const QVector<quint16> &value这部分新手最容易懵,我们拆开讲:QVector:Qt 框架提供的动态数组容器,相当于一个可以自动变长的数组,用来存一组同类型的数据,类似 C++ 标准库里的std::vector。<quint16>:模板参数,说明这个QVector数组里,每个元素都是quint16类型。quint16是 Qt 定义的标准类型:无符号 16 位整数,对应原生 C++ 的unsigned short,占 2 个字节,取值范围 0~65535,工业通信里一般用来存寄存器的原始数据。
&:引用符号,这里表示 “传引用”,而不是 “传值”。- 好处:不用把整个数组复制一份,直接操作原数据,效率很高(尤其是数组很大的时候)。
const:常量修饰符,和引用搭配叫 “常引用”。- 作用:保证这个数组在函数里只能读、不能改,防止代码不小心修改了原始数据,同时也是 C++ 的规范写法。
value:参数名,就是传进来的原始数据数组。
第 2 行:函数体开始
{大括号{ }用来包裹函数的所有执行代码,左大括号代表函数逻辑开始,最后一行的右大括号代表函数结束。
第 3 行:消除未使用参数警告
Q_UNUSED(base_address);Q_UNUSED():Qt 提供的一个宏(可以理解成 Qt 预先定义好的语法标记)。- 作用:告诉编译器:“
base_address这个参数我是故意不用的,不要给我报‘未使用的参数’的警告”。 - 为什么传了又不用?这个函数一般是统一的接口规范(比如所有同类控件的更新函数都必须有基地址这个参数),但
TemperatureBar这个控件自己的逻辑用不到基地址,所以用这个宏来消除编译警告,不影响程序运行。
第 4 行:计算数据在数组中的偏移位置
int offset = m_address - m_base_address;int offset:定义一个整数类型的局部变量,名字叫offset,意思是 “偏移量 / 下标”。=:赋值符号,把右边的计算结果存到左边的变量里。m_address和m_base_address- 这两个都是
TemperatureBar类的成员变量(m_前缀是 C++/Qt 的通用命名规范,是member的缩写,用来一眼区分 “类的成员变量” 和 “函数里的临时变量”)。 m_address:当前这个温度条对应的寄存器地址。m_base_address:这一批数据对应的寄存器起始基地址。
- 这两个都是
- 计算逻辑:
当前地址 - 起始基地址 = 这个温度条的数据在数组里的下标位置。 举个例子:基地址是 100,当前温度条地址是 103,那偏移量就是 3,对应数组里第 3 个元素(数组下标从 0 开始数)。
第 5 行:数组下标越界安全判断
if (offset >= 0 && offset < value.size()) {这是非常重要的安全防护代码,新手最容易漏掉这一步导致程序崩溃。
if (条件) { ... }:条件判断语句,只有括号里的条件成立(结果为真),才会执行大括号里的代码。- 条件由两部分组成,用
&&连接&&:逻辑与运算符,意思是 “并且”,只有左右两个条件都成立,整体条件才成立。- 第一个条件:
offset >= 0偏移量不能是负数。因为数组下标从 0 开始,负数下标是非法的,会访问到错误内存。 - 第二个条件:
offset < value.size()value.size():QVector的成员函数,返回数组里元素的总个数。- 意思是:偏移量必须小于数组总长度。比如数组有 5 个元素,长度是 5,合法下标是 0~4,所以 offset 必须小于 5。
- 整行作用:确保要访问的数组下标是合法的,防止数组越界(访问数组外面的内存)导致程序直接崩溃。只有偏移量在合法范围内,才去更新数据。
第 6 行:取出对应位置的原始数据
quint16 raw_value = value.at(offset);quint16 raw_value:定义一个 16 位无符号整数的局部变量,叫raw_value(原始值),用来存从数组里取出来的寄存器原始数据。value.at(offset)at()是QVector的成员函数,作用是获取数组中下标为offset的那个元素。- 补充:QVector 取值有两种写法:
value[offset]和value.at(offset)。at()会自带越界检查,越界时会安全报错;[]运算符越界会直接崩溃,所以这里用at()是更规范安全的写法。
- 整行意思:从数据数组里,取出偏移量对应位置的原始 16 位数据,存到
raw_value变量里。
第 7 行:初始化显示值
float display_value = raw_value;float display_value:定义一个单精度浮点型变量,叫display_value(显示值),也就是最终要显示在温度条上的真实数值。= raw_value:把原始整数值直接赋值给浮点型变量。- 这里会发生隐式类型转换:自动把整数的
quint16转成带小数的float。比如原始值是 25,转完就是 25.0。
- 这里会发生隐式类型转换:自动把整数的
- 为什么要用浮点型?因为温度值经常带小数(比如 25.6℃),整数存不了小数,所以需要用浮点型来存最终的显示值。
第 8 行:判断是否需要数值缩放
if (m_is_shift) {m_is_shift:类的成员变量,bool布尔类型(只有true真 /false假 两个值)。- 意思是 “是否需要移位 / 缩放”,也就是控制 “原始值要不要除以 10 来还原真实温度” 的开关。
- 为什么要有这个开关?工业通信里,很多传感器会把温度放大 10 倍 / 100 倍后用整数传输(比如 25.6℃存成整数 256),但也有的传感器直接传整数温度。所以用这个开关来兼容两种情况,初始化控件的时候设置好就行。
第 9 行:数值缩放,还原真实温度
display_value = raw_value / 10.0f;- 给
display_value重新赋值,覆盖掉之前的原始值。 raw_value / 10.0f:原始值除以 10.0。- 重点注意
10.0f:末尾的f表示这是float类型的浮点数。 - 如果写成
raw_value / 10(整数除以整数),那就是整数除法,会直接丢掉小数部分,比如256 / 10 = 25; - 写成
10.0f就是浮点数除法,会保留小数,256 / 10.0f = 25.6,这才是我们要的真实温度。
- 重点注意
- 整行意思:如果开启了缩放,就把原始整数除以 10,得到带小数的真实温度值。
第 10 行:缩放判断结束
}右大括号,对应第 8 行if (m_is_shift)的左大括号,缩放逻辑的代码块到此结束。
第 11 行:更新温度条控件的显示
set_value(display_value);set_value():这是TemperatureBar类(或者它的父类)的成员函数。- 作用:把计算好的真实数值设置给温度条控件,更新界面上的显示 —— 比如更新进度条的长度、更新旁边的数字显示等等。
- 这是整个函数的最终目的:前面所有计算都是为了得到正确的数值,最后调用这个函数完成界面更新。
第 12 行:合法数据处理结束
}右大括号,对应第 5 行if (offset >= 0 && ...)的左大括号,合法数据的处理逻辑到此结束。 如果偏移量不合法,这一大段代码都会直接跳过,什么都不做,保证程序安全。
第 13 行:函数结束
}最外层的右大括号,整个update_data函数的代码到此全部结束。
整体执行流程梳理(帮你串起来理解)
这个函数从调用到结束,完整走一遍是这样的:
- 外部传入「寄存器基地址」和「一批寄存器原始数据数组」。
- 计算当前温度条对应的数据,在数组里的下标位置。
- 检查下标是否合法,不合法就直接结束,防止崩溃。
- 合法的话,取出对应位置的原始 16 位整数。
- 根据控件的缩放开关,决定要不要把原始值除以 10,得到真实的温度显示值。
- 把最终数值设置给温度条控件,更新界面显示。
补充:两个重要的代码规范
m_前缀命名:类的成员变量统一加m_前缀,是行业通用规范。好处是不用翻定义,一眼就能分清 “哪个是类的成员变量”“哪个是函数里的临时局部变量”。- 越界保护:只要操作数组 / 容器,就一定要先判断下标是否合法,这是避免程序崩溃的最基础也最重要的习惯,这段代码的写法非常标准。
编程学习
技术分享
实战经验