静态检查是比较好的一种自动检查代码工具,可以发现一些隐藏问题,当然更多是让你的代码更加规范,更加在可控范围内。

以下是我整理的错误,也是自己对C++进一步的思考

  • 构造函数显式调用问题

提示:

Class 'CLBTimeSpan' has a constructor with 1 argument that is not explicit. Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided.

翻译:

类'CLBTimeSpan'有一个带有1个参数的构造函数,它不是显式的。 出于类型安全的原因,这些构造函数通常应该是明确的。 在构造函数中使用explicit关键字意味着可以避免在使用类时出现一些错误。

  具体源码:

原因分析:

1.     构造只有一个参数时,容易被隐式调用,这样容易导致初始化错误。

例如:

CLBTimeSpan dtSpan = 0;  //隐式调用其构造函数,默认调用

2.     要使用“explicit” 关键字进行约束

改进要点:

改成:explicit CLBTimeSpan(const time_t &t);

改成这样之后

CLBTimeSpan dtSpan = 0; //编译错误,不能隐式调用其构造函数

CLBTimeSpan dtSpan(0);  //显式调用成功

扩展阅读:

C++中,一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数),承担了两个角色。1 是个构造器 ,2 是个默认且隐含的类型转换操作符。

所以,有时候在我们写下如 AAA a = XXX,这样的代码,且恰好XXX的类型正好是AAA单参数构造器的参数类型,这时候编译器就自动调用这个构造器,创建一个AAA的对象。

这样看起来好象很酷,很方便。但在某些情况下,却违背了我们(程序员)的本意。这时候就要在这个构造器前面加上explicit修饰,指定这个构造器只能被明确的调用/使用,不能作为类型转换操作符被隐含的使用。

  • 构造函数直接赋值

提示:

When an object of a class is created, the constructors of all member variables are called consecutively in the order the variables are declared, even if you don't explicitly write them to the initialization list. You could avoid assigning 'm_timeSpan' a value by passing the value to the constructor in the initialization list.

翻译:

创建类的对象时,即使您没有将它们显式写入初始化列表,也会按声明变量的顺序连续调用所有成员变量的构造函数。 您可以通过将值传递给初始化列表中的构造函数来避免为'm_timeSpan'赋值。

对应的源码:

  

原因分析:

这个性能改进项,主要意思就是通过构造函数对成员变量进行初始化的,建议使用直接初始化,不用调用=进行初始化。当然输入参数,改成引用效果更佳

改进要点:

CLBTimeSpan::CLBTimeSpan(consttime_t &t): m_timeSpan(t)

{

//m_timeSpan = t;

}

扩展阅读:

其实在构造函数里面调用等于号并不是真正意义上的“初始化”(未改进之前的方式)。这个过程相当于:

1. 系统创建成员变量;

2. 创建完后再进行赋值操作。

而在构造函数后面跟冒号,就相当于:

1. 系统创建成员变量并且初始化。也就是系统为成员变量分配了一块内存并且把相应的数据给填了进去。而构造函数里面调用等于号的方式是分配好后再进行赋值,多了一个步骤。

  • 虚函数的作用范围

提示:

Virtual function 'Open' is called from constructor 'CBackFile(char*strFileName,uint nOpenFlags)' at line 53. Dynamic binding is not used.

翻译:

虚函数'Open'在第53行从构造函数'CBackFile(char * strFileName,uint nOpenFlags)'调用。不使用动态绑定。

  对应源码位置:

  virtual bool Open(char *strFileName, uint nOpenFlags);  

原因分析:

Open是虚函数,如果不是用于接口定义的话(也就是不存在动态绑定)且本身是有实现的话,建议是不用使用虚函数。

这个虚函数本身是作用为了将来派生用的(派生类会)。但实际代码,的确未使用动态绑定的特性。另外虚函数是会占一定空间,导致编译之后的可执行文件体积变大,效率方面也是有所降低,这里不推荐使用虚函数。

  改进要点:

扩展阅读:

一般虚函数是用于“实现多态性(Polymorphism),多态性是将接口与实现进行分离”还有就是析构函数这里经常要用到虚函数。

但是我们更应该要知道虚函数的缺点:

如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。因为它会为类增加一个虚函数表,对象增加一个虚指针,使得对象的体积增大。所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。

同理,也可以知道如果不是当基类用途的类,析构函数也是不需要使用虚函数的。

实验证明,如果程序里减少虚函数,可以缩减可执行文件体积大小。

  • 构造函数里未完全初始化成员变量

提示:

Member variable 'CLBTime::m_status' is not initialized in the constructor.

翻译:

成员变量'CLBTime :: m_status'未在构造函数中初始化。

也有可能这样提示:(同一个意思)

When an object of a class is created, the constructors of all member variables are called consecutively in the order the variables are declared, even if you don‘t explicitly write them to the initialization list. You could avoid assigning ‘m_strHrefOnPanReady‘ a value by passing the value to the constructor in the initialization list.

翻译:

创建类的对象时,即使您没有将它们显式写入初始化列表,也会按声明变量的顺序连续调用所有成员变量的构造函数。 您可以通过将值传递给初始化列表中的构造函数来避免为“m_strHrefOnPanReady”赋值。

对应源码位置:

原因分析:

这个问题比较直接,就是成员变量未初始化。

改进要点:

扩展阅读:

一般来说构造函数用来初始化成员变量(这是个好习惯),但如果其中有个变量未初始化,也存在很大风险。所以要核对是否所有的成员变量都初始化(比较安全的数)

  • 成员变量是需要动态分配内存

  标题可能有点问题,大概意思是成员变量有指针,那么要重载=

提示:

Class 'CLogFile' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s).

翻译:

类'CLogFile'没有operator =,因为它具有动态内存/资源分配,所以建议使用它。

  对应源码位置:

原因分析:

意思是构造函数有动态分配内存,所以建议该类增加operator =函数。为什么呢?

因为是存在动态分配,因此如果默认调用A=B的时候,会默认调用浅拷贝,就好比只是B里成员变量的指针拷贝给A里成员变量,而不是拷贝“成员变量指针指向内容”,这样就会带来指针被多次引用,指向地方又是同一个地方,存在很大风险。

改进要点:

增加CLogFile 的operator =函数

扩展阅读:

1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

这个重载“=”,使用

CExample theObjthree;

theObjthree.init(60);//现在需要一个对象赋值操作,被赋值对象的原内容被清除,并用右边对象的内容填充。

theObjthree = theObjone;  //此时调用“=”操作符,若CExample内有申请操作,此时 theObjthree里的指针是指向 theObjone,从而忘记里对theObjthree内空间释放导致内存的泄露。

  • 指针类型缺少强制转换

  提示:

C-style pointer casting detected. C++ offers four different kinds of casts as replacements: static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to any of those automatically, thus it is considered safer if the programmer explicitly states which kind of cast is expected. See also: https://www.securecoding.cert.org/confluence/display/cplusplus/EXP05-CPP.+Do+not+use+C-style+casts.

翻译:

检测到C风格的指针转换。 C ++提供了四种不同类型的转换作为替换:static_cast,const_cast,dynamic_cast和reinterpret_cast。 C风格的强制转换可以自动评估任何一个,因此如果程序员明确说明预期哪种类型的强制转换,则认为它更安全。 另请参阅:https://www.securecoding.cert.org/confluence/display/cplusplus/EXP05-CPP.+Do+not+use+C-style+casts。

原因分析:

不要用C风格进行强制转换

static_cast,const_cast,dynamic_cast和reinterpret_cast 替换原先

改进要点:

扩展阅读:

实际上static_cast,const_cast,dynamic_cast和reinterpret_cast都是用于C++类型强制转换,主要用于继承。

static_cast 应该比较频繁,static_cast主要是一些类型转换。static_cast相当于传统的C语言里的强制转换,该运算符把expression转换为new_type类型,用来强迫隐式转换,例如non-const对象转为const对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:

①    用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。

进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;

进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。

②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

③把空指针转换成目标类型的空指针。

④把任何类型的表达式转换成void类型。

注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。

  • 缺少拷贝构造函数

  这里是指当该类存在指针的成员变量时,需要增加拷贝构造函数

提示:

Class 'CLocalSocket' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).

翻译:

类'CLocalSocket'没有建议(默认)的复制构造函数,因为它具有动态内存/资源分配。

原因分析:

首先确定的是C++中每个类一定有一个拷贝构造函数(好习惯)。它可以用来创建一个对象,并用另一个对象的数据初始化新建对象。你的代码中如果没有显示定义拷贝构造函数(用来实现你自己的一些特殊需求),则C++将为每个类隐式地提供一个缺省的拷贝构造函数。缺省的拷贝构造函数简单地将参数对象的每个数据域复制给新建对象中相应的副本(注意如果数据域是一个指向其他对象的指针,那么仅简单地复制指针保存的地址值,而不是复制对象的内容)(会产生浅拷贝,因此成员变量有动态内容)

改进要点:

增加CLocalSocket 的拷贝构造函数

扩展阅读:

拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。

在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):

1) 一个对象作为函数参数,以值传递的方式传入函数体;

例如:BOOL testfunc(CExample obj);

2) 一个对象作为函数返回值,以值传递的方式从函数返回;

CTest func()

{

CTest theTest;

return theTest;

}

3) 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);

CExample theObjtwo = theObjone; //注意此时调用的不是重载“=”

还有:CExample theObjthree(theObjone);。//显式调用

  • 减少变量的作用域

其实的意思,有些变量没有必要在前面就申请。建议用到之前申明即可

 提示:

The scope of the variable 'byDir' can be reduced.

翻译:

可以减少变量'byDir'的范围。

原因分析:
byDir 实际上就是在for循环内部使用,建议在for循环进行初始化。(个人认为这个不是很好的建议,会增加构造函数调用次数,实际情况请适当改进)
改进要点:for (int i = 0; i < 0x0040; i++){byte byDir = (byte)((i >> 4) & 0x0F);     //高4,表 abcbyte byNum = (byte)(i & 0x0F);                //低3,表 数字sprintf(strUPath, "/media/sd%c%d/", strUDiskDir[byDir],byNum);if (Utility::IsExist_Dir(strUPath)){nRes = i;break;}}

  • 迭代器使用后置叠加或减运算

  增加效率的问题

提示:

Prefix ++/-- operators should be preferred for non-primitive types. Pre-increment/decrement can be more efficient than post-increment/decrement. Post-increment/decrement usually involves keeping a copy of the previous value around and adds a little extra code.

翻译:

前缀++ /  - 运算符应该是非基本类型的首选。 预增量/减量可以比后增量/减量更有效。 后递增/递减通常涉及保留前一个值的副本并添加一些额外的代码。

原因分析:

迭代器前置++和后置++的运行效率是不同的,前置++效率更高,因为后置运算符需要做一个临时的类对象拷贝。

这里是不是比较坑?

如果是后置:

调用过程(从汇编语言可以分析)

a.     先拷贝*this内容到_temp中;b. 调用前置++操作符,自增*this的内容;c. 返回_temp;

b.     从过程就可以轻易看出,后置++在前置++的基础上,至少多了步骤a和c;

如果是前置:

调用过程(从汇编语言可以分析)

a.     a.直接递增;c. 返回(*this);

前置式迭代器不需要传回旧值,所以不需要花费一个临时对象来保存旧值。因此,面对任何迭代器(以及任何抽象数据型别),应该优先使用前置式。这条规则对递减操作也同样适用。

  • 直接在函数参数中使用C风格字符串

提示:

The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly passing the string.

翻译:

从c_str()返回到std :: string的const char *的转换会创建一个不必要的字符串副本。 通过直接传递字符串来解决这个问题。

原因分析:

比如一个函数的参数类型是string,调用时实参直接使用了C风格(c_str())的字符串,于是就会有以上提示,主要还是因为这里会造成一个不必要的字符串拷贝,降低运行效率。

这里也有坑:大家可以度娘下 c_str()

扩展阅读:

char* c;

string s="1234";

c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理,同时,编译器也将报错——将一个const char *赋与一个char *。野指针无意之中出现,很尴尬。

应该这样用:

char c[20];

string s="1234";

strcpy(c,s.c_str());

这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作

这样说吗c_str()会产生一个临时空间,而且调用完c_str()就无效了。存在效率问题,也存在使用风险,好在大部分编译器能报错

  • 使用无用的find

  提示:

Either inefficient or wrong usage of string::find(). string::compare() will be faster if string::find's result is compared with 0, because it will not scan the whole string. If your intention is to check that there are no findings in the string, you should compare with std::string::npos.

翻译:

string :: find ()的低效或错误用法。 如果将string :: find的结果与0进行比较,则string :: compare()会更快,因为它不会扫描整个字符串。 如果您打算检查字符串中没有发现,则应与std :: string :: npos进行比较。

原因分析:

find() 效率低,要用compare

代码本身不会出错,但是效率上是不被认可的,如cppcheck所说,如果你希望检查某个字符串是不是某个期望的字符串,那你应该使用compare函数,因为这样更快。

扩展阅读:

很多时候,我们会写的find代码,值不等于-1则说明找到了该字符串,这样做效率比较低,可以用compare比较

C++标准库里面的string::rfind和string:find不是用快速匹配算法实现的,效率不是一般的差

在实践中还发现,string::find(char)比较string::find(string)慢很多

  • 函数参数使用传值而不是传引用

提示:

Parameter 'strShowTime' is passed by value. It could be passed as a const reference which is usually faster and recommended in C++.

翻译:

参数'strShowTime'按值传递。 它可以作为const引用传递,通常更快并且在C ++中推荐。

原因分析:

值传递会产生赋值操作,会产生临时变量,因此效率比较低(对于超过4字节的结构体、字符串)

可以改成 const & 方式

  • 使用memset清空含有string(wstring)类型成员的结构体

提示:

Using 'memset' on struct that contains a 'std::wstring'. [memsetClass]

翻译:

原因分析:

在C语言中,使用memset清空内存是常用操作,在C++中,和malloc一样,使用memset直接操作内存是有很大风险的,因为它们都只是在内存层面做了改动,但是对类对象本身却没有任何行动,也就是说,他们不会调用类对象的构造或者析构函数,如果我们的结构体中含有string类型的成员,直接使用memset很可能导致内存泄漏!

这里涉及到一个POD参考的概念。如果一个类不是POD的,那么就不应该使用如mem*,malloc等内存操作函数,否则,我们将得不到我们想要的东西,甚至引发错误。

扩展阅读:

测试程序以memcpy函数为例,运行本程序,虽然tt2可以正常的将str1拷贝进来,但是最后程序奔溃了!!

想想原因是为何?

程序崩溃在tt的析构函数中,因为无法探究memcpy到底做了哪些具体操作,所以我们可以猜测它是将tt的内存区域完整的拷贝到了tt2内,但是,tt2中的string类型的成员str1并没有完成自己的构造,而是通过内存拷贝的方式完整的使用了tt的数据,那么执行完delete ,tt2的str1也会析构自身,这时其析构的其实是tt的str1。等到tt再去析构自己时,奔溃就发生了!以上只是自己的猜测。可能不是事实,但是,当我把delete tt2注释后,崩溃就消失了,这也能证明自己上面的论述。

在C++中,除非是明确的POD类型,否则放弃使用mem*系包括malloc等传统C内存操作函数。

首先,我们需要弄明白,我们为什么需要使用memet,因为我们需要将这个结构体的数据清零,所以我们真正需要的是一个结构体(类)的构造函数!在类中写一个结构体,将类里的所有成员变量进行列表初始化就可以解决这个问题了。话说回来,就好像,我们在写C代码时, 如果结构体某个成员类型需要是结构体类型,我们使用了该结构体指针一样,我们同样可以使用string类型的指针来表示该成员类型。毕竟在VS2010环境下,一个string类型的内存占用就是32byte,而一个string*只有4byte。如果担心hold不住指针类型,可以使用智能指针来折中,如shared_ptr,内存的占用将减小到8。事实上,使用智能使用已经是一个最优方案了。

Ps:string不是常规类型,使用时不能简单等同于“int”之类进行操作,否则很多坑的

  • size() 判断是否为空,效率低

提示:

Checking for 'm_listSendBuffer' emptiness might be inefficient. Using m_listSendBuffer.empty() instead of m_listSendBuffer.size() can be faster. m_listSendBuffer.size() can take linear time but m_listSendBuffer.empty() is guaranteed to take constant time.

翻译:

检查'm_listSendBuffer'空虚可能效率低下。 使用m_listSendBuffer.empty()而不是m_listSendBuffer.size()可以更快。 m_listSendBuffer.size()可以占用线性时间,但m_listSendBuffer.empty()保证需要恒定的时间。

在vocter里empty()的效率要高于size

扩展阅读:

size_type size() const 
{  

size_type __result = 0;

distance(begin(), end(), __result);

return __result;

}

常见Cppcheck检查问题总结(静态检查错误)相关推荐

  1. c语言程序只能调试成功一半,c语言程序的调试方法所谓程序调试是指对程序的查错和排错。调试程序一般应经过以下几个步骤:一、先进行人工检查,即静态检查。在写好一个程序以后,不要匆匆忙忙...

    c语言程序的调试方法 所谓程序调试是指对程序的查错和排错. 调试程序一般应经过以下几个步骤: 一.先进行人工检查,即静态检查. 在写好一个程序以后,不要匆匆忙忙上机,而应对程序进行人工检查.这一步十分 ...

  2. cppcheck linux,cppcheck实现c++代码静态检查

    本文案旨在输出方法: 通过jenkins集成cppcheck实现对c++代码的检查,并输出报告,通过报表可以明确分析出问题 Cppcheck是c/c++代码的静态分析工具.它提供了独特的代码分析来检测 ...

  3. php代码静态检查工具,代码静态检查工具汇总

    工具名 静态扫描语言开源/付费 厂商 介绍 主页网址 ounec5.0 VB.Net.C.C++和C#, 还支持Java. 付 费 Ounce Labs \ http://www.ouncelabs. ...

  4. 代码静态检查之findbugs

    白盒测试中的静态检查一般是检查编码标准规范,错误列表.编码规范往往团队会根据自己的经验和风格进行设置一些规范.现在很多IDE工具都会在编辑代码的时候实时的提醒是否符合代码风格.错误列表,一般是代码潜在 ...

  5. C/C++代码缺陷静态检查工具cppcheck

    cppcheck介绍和安装 CppCheck是一个C/C++代码缺陷静态检查工具.静态代码检查是检查代码是否安全和健壮,是否有隐藏问题. CppCheck只检查编译器检查不出来的bug,不检查语法错误 ...

  6. cppcheck 自定义规则_cppcheck代码静态检查工具及相关工具插件用法介绍

    摘要:介绍代码缺陷静态检查工具(static code analyzer)cppcheck,以及其vs.qtcreator.git.jenkins插件及用法. Cppcheck着重于检测未定义的行为和 ...

  7. linux shell脚本 静态检查工具 shellcheck 简介

    简介 shellcheck 是一款实用的 shell脚本静态检查工具. 首先,可以帮助你提前发现并修复简单的语法错误,节约时间.每次都需要运行才发现写错了一个小地方,确实非常浪费时间. 其次,可以针对 ...

  8. React Native工程中TSLint静态检查工具的探索之路

    背景 建立的代码规范没人遵守,项目中遍地风格迥异的代码,你会不会抓狂? 通过测试用例的程序还会出现Bug,而原因仅仅是自己犯下的低级错误,你会不会抓狂? 某种代码写法存在问题导致崩溃时,只能全工程检查 ...

  9. 软件构造-Reading 1:静态检查

    大纲: 阅读1:静态检查 目标: 冰雹序列 计算冰雹序列 类型 静态类型 静态检查.动态检查.无检查 惊喜:原始类型并不是真正的数字! 阅读练习 数组和集合 迭代 方法 变化的值与重新分配变量 记录假 ...

最新文章

  1. vue中点击导航栏部分,页面切换
  2. Auth模块、Forms组件
  3. SQL SERVER 如果判断text类型数据不为空
  4. use resources at the campus if possible
  5. Oracle中5个核心Sql语句的基本构造:Select、Insert、Update、Delete和Merge
  6. (BCB) CComPtrIHTMLDocument2 FIEDoc;
  7. 拓端tecdat|matlab实现MCMC的马尔可夫转换MS- ARMA - GARCH模型估计
  8. unity 找到的对象是动态加载,且两个不同的父物体上都有要的那个对象并有相同的名子
  9. 阿里云盘扩容时,容量限制是多少?
  10. rsync+inotify实现数据实时同步
  11. Fedora9中安装中文输入法
  12. np学习——OSPF的典型配置案例
  13. lisp 焊接符号标注_焊接符号标注及表示方法-详解aws焊接符号、钢结构焊接符号含义大全...
  14. 深入理解GatewayWorker框架
  15. PyTorch:view() 与 reshape() 区别详解
  16. display的contents属性
  17. 码隆科技在 CVPR 2019 主办商品识别大赛,等你来战!
  18. Unity3d网络总结NetWork组件使用(总结篇)
  19. 中科院大学计算机科学与技术王伟强,王伟强-中国科学院大学-UCAS
  20. web开发技巧-网页排版布局常见问题及解决办法

热门文章

  1. c店banner的一个宽高度范围,详情页的宽高度是。淘宝天猫的详情页,看尺寸的快捷键,banner的设计规则和技巧
  2. Linux九阴真经之无影剑残卷7(进程管理)
  3. 广州融媒体峰会现场直播中,BirdDog Full NDI应用有哪些优点?难点?如何解决?
  4. 在CentOS 7.7 x86_64上安装python3.11.0实录
  5. 学生党的论文下载方法
  6. UPC——2020年春混合个人训练第二十四场(DEFG)
  7. 潘粤明版《鬼吹灯》口碑炸裂!豆瓣8.3高分到底好看在哪?
  8. 血族群机器人_【lay兴】 《Heartbeat》[上]古老血族 X 智能AI丨 Can you feel my heartbeat?...
  9. 【Visial Studio疑难杂症】“变量已被优化掉,因而不可用”解决方案
  10. sysbench 压测 安装