c++静态局部变量

项目场景:

       Qt 应用程序可以记录can总线监听到的帧,帧可以同时展示在不同的视图上,每个视图都提供实时保存和单次保存功能。程序设计的时候,是界面和业务代码分离的,实时保存和单次保存的功能由一个文件保存模块实现,界面通过文件保存模块的DLL API实现保存数据到文件的功能。界面视图是相互独立的,可以同时展示数据,可以同时进行实时保存和单次保存,理论上保存数据应该完全独立,不能相互干扰,每个视图在设计的时候,都是使用自己的一份对象进行保存,依赖不同对象来实现资源独立管理,避免使用同一对象在操作时,因为一些状态信息处理不到位,导致不可预期的结果。同时,文件保存模块DLL是导出一个类的,类对象内部有做多线程保护,文件的读写使用的资源也是按单个文件相互独立的。从设计上,界面设计和DLL设计都是没问题的。

问题描述:

       每个视图单独使用的时候,程序的实时保存和单次保存功能都是完全正确的,不同视图单次保存文件也是没有问题的,而且不同视图同时实时保存成不同类型的文件时,功能也是完全正确的,问题出在不同视图同时进行同一类文件保存时,记录的数据存在错乱现象。 DLL API写数据代码:
        int FileOp::WriteDataToAsc(Frame one_frame)
        {
            static last_frame_timestamp = 0;
            ...
            UINT64 delta_time = one_frame - last_frame_timestamp;
            last_frame_timestamp = one_frame.timestamp;
            ...
            //把解析后的帧信息写入文件
            ...
        }
代码本身很简单,只是把帧信息写入文件,由于需要记录上一帧的时间戳,便于计算前后两帧的时间差delta_time,引入了一个局部的静态变量last_frame_timestamp。程序在多个视图同时写asc类型的文件时,会调用这个函数,最终写入文件时delta_time的数据和预想的不一致。

原因分析:

       同一个对象在调用这个函数时是正常的,多个对象同时调用这个函数却出现了错误。通过走读代码,没发现业务逻辑有问题,转而通过日志输出来协助分析问题。通过日志信息发现,帧信息是没问题的,但是delta_time的计算结果却不正确,最终锁定了问题根源:static last_frame_timestamp这个变量有问题。仔细回想一下静态变量的机制,突然就豁然开朗了,静态变量确实能记录上一次调用时记录的值,但是静态变量在内存中只有一份,不同对象其实是共用这一个内存对象的,所以不同对象同时使用这个变量记录数据就会导致数据错乱。

解决方案:

       解决问题的方案是使用类成员变量代替静态局部变量。C++的特性之一就是封装,把属性和操作封装在一个类当中,对外只提供public的接口与类对象交互,所以写C++代码是几乎不用全局变量的,静态成员也只限于类的静态成员函数和静态成员变量,在非静态成员函数中使用静态变量本身已经破坏了C++的封装特性,是一个非常坏的变量使用方法,因为局部静态变量完全可以使用成员变量代替,也能起到记录旧数据值的目的,同时没有违反C++的封装特性,类对象完全能保证数据独立,不会引入非预期的问题。

总结提高:

       从问题的解决方案可以知道,在C++编程中,是需要极力避免使用局部静态变量的,因为它打破了C++的封装性,不能保证每个对象能有效管理好属于本对象的资源,静态数据对象是不属于某个对象的,这打破了程序设计之初,依靠不同对象管理不同资源的设想。 不过,静态局部变量也有它的用武之地,比如单例模式,就使用了一个获取对象的静态成员函数,返回一个对象,其内部使用的,就是一个局部静态变量。 既然在C++中,一般只使用静态成员变量和静态成员函数,那下面也介绍一下他们的相关知识。

C++静态成员变量

普通成员变量与静态成员变量的区别:
1、内存位置不同
       静态成员变量是属于类的,不属于某一个类对象,所有类对象共用一份静态成员变量,它们保存在内存的静态区,并不占用类的内存空间。当静态成员变量的值改变时,所有对象都会受到影响,而非静态成员变量保存在栈内存或堆内存中,在某个对象改变时,并不影响其他对象的非静态成员变量的值。
2、访问方式不同
       静态成员变量既可以通过对象访问,也可以通过类名访问,而非静态成员变量则必须通过对象访问。
3、初始化方式不同
       静态成员变量必须初始化,而且必须在类外初始化,非成员变量没有强制要求初始化。在实际开发过程中,类成员的初始化工作一般放在构造函数体或初始化列表,如果使用未初始化的成员变量可能导致不可预期的结果,尤其是指针成员变量,野指针或空指针都不是我们想要的,所以,初始化工作是必须严肃对待的一件事情。

C++静态成员函数

普通成员函数和静态成员函数的区别:
1、调用方式不同
       普通成员函数只能通过对象访问,程序在编译时,编译器会为普通成员函数增加一个隐式的形参this,在函数调用时把对象的地址赋值给this,所以普通成员函数只能通过对象调用;静态成员函数可以通过类访问,编译器生成目标代码时,不会增加隐式的形参this指针,所以可以不依赖对象而直接使用类名访问。
2、访问的范围不同
       普通成员函数可以访问类的所有内容,包括所有成员函数和所有成员变量;静态成员函数只能访问静态成员变量和静态成员函数。由于静态成员函数不含隐式的this指针,所以无法访问具体对象的非静态成员(非静态成员变量或非静态成员函数)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值