C++编程新手容易犯的10种编程错误

嵌入式技术

1368人已加入

描述

 公司每年都会有一定的人员流动,相应地也会招一些应届生补充进来,指导应届生已经成为老员工的必修课了。平日里会我们会经常帮新人排查代码中的问题,在此过程中发现了 C++ 新手容易犯的一些编程错误,在此简单的总结一下,给新人们提供一个参考。

1、有些关键字在 cpp 文件中多写了

对于 C++ 类,一些关键字只要写在 .h 中就好,cpp 中就不用再加上了,比如 virtual、static 等关键字,如果在 cpp 中多写,编译器会报错。比如如下的虚接口与静态成员变量的定义,只要在头文件中声明就可以了。

	class shape {     virtual Draw();     //...     static int nLevel; } 

2、函数参数的默认值写到函数实现中了

带有参数默认值的函数,默认值是加在函数声明处的,函数实现处的参数是不需要带上的。为了方便查看代码,在函数实现处的参数中,将默认值注释起来。正确的做法是,头文件中有默认值:

	BOOL CreateConf( const CString& strConfName, const BOOL bAudio = FALSE ); 在函数实现处的参数中不用添加默认值: BOOL CreateConf( const CString& strConfName, const BOOL bAudio/* = FALSE*/ ); {     // ...... } 

3、在编写类的时候,在类的结尾处忘记添加 ";" 分号了

在类的结尾处忘记添加分号,编译会报错,新人们有可能找了半天也没找出引起编译错误的原因。其实很简单,在类的结尾处忘记添加分号了。

	class Shape {     // ... }; 

4、只添加了函数声明,没有函数实现

在添加类的函数时,只在类的头文件中添加了函数声明,但在 cpp 中却没有添加函数的实现。如果其他地方调用到该函数,在编译链接的时候会报unresolved external symbol错误。因为没有实现,所有没有供链接使用的 obj 文件。

5、cpp 文件忘记添加到工程中,导致没有生成供链接使用的 obj 文件

在添加 C++ 类时,我们一般会添加 .h 头文件和一个 .cpp 源文件。结果忘记把 .cpp 文件添加到工程中了,即没有参与编译,没有生成供链接使用的 obj 文件。如果有代码调用到该 C++ 类的接口,则在编译链接的时候会报unresolved external symbol错误,即链接不到该 C++ 类对应的接口。

6、函数中返回了一个局部变量的地址或者引用

在函数中返回了一个局部变量的地址或者引用,而这个局部变量在函数结束时其生命周期就结束了,内存就被释放了。当外部访问到该变量的内存,会触发内存访问违例的异常,因为该变量的内存已经释放了。比如如下的错误代码:

	char* GetResult() {     char chResult[100] = { 0 };     // ......     return chResult; } 

7、忘记将父类中的接口声明 virtual 函数,导致多态没有生效

代码中本来要借助于 C++ 多态的虚函数调用,调用子类实现的接口,结果忘记在父类中将对应的接口声明为 virtual,导致没有调用到子类实现的函数。一定要记住,要实现多态下的函数调用,父类的相关接口必须声明为 virtual。

	class Shape() {     // ...     virtual void Draw();     // ... } 

8、该使用双指针的地方,却使用了单指针

有时我们需要调用一个接口去获取某些数据,接口中将数据拷贝到传入的参数对应的内存中,此时设计参数时会传入指针或引用。我们在调用GetData 之前定义了结构体指针p,并 new 出了对应的结构体对象内存,应该在定义 GetData 接口时应该使用双指针(指针的指针)的,结果错写成了单指针。有问题的代码如下:

	struct CodecInfo     // 编码信息 {     int nFrameRate;     // ... } CodecInfo* pInfo = new CodecInfo; GetAudioCodecPtr()->GetCodecInfo(pInfo);   // 调用AudioCodec::GetCodecInfo获取编码信息 AudioCodec::GetCodecInfo( CodecInfo* pInfo)  // 此处的参数不应该使用单指针 {     memcpy(pInfo, m_codecInfo, sizeof(CodecInfo)); } 上面中的AudioCodec::GetCodecInfo接口的参数不应该为单指针,应该用双指针,修改后的代码应该如下:

	AudioCodec::GetCodecInfo( CodecInfo** pInfo)  // 此处的参数类型使用双指针 {     memcpy(*pInfo, m_codecInfo, sizeof(CodecInfo)); } 

9、发布 exe 程序时,忘记将 exe 依赖的 C 运行时库和 MFC 库带上

比如新人用 VS-MFC 库编写一个测试用的工具软件,结果在发布 release 版本程序时,没有将程序依赖的 C 运行时库带上,导致该工具软件在某些电脑中启动报错,提示找不到 C 运行时库:因为程序中依赖了动态版本的运行时库和 MFC 库,在发布程序时要将这些库带上。有些系统中没有这些库,程序启动时就会报找不到库,就会启动失败。

10、应该使用深拷贝,却使用了浅拷贝

本来应该要进行深拷贝的,却使用了浅拷贝(直接赋值),导致另个不同生命周期的 C++ 对象指向了同一块内存,一个对象将内存释放后,另一个对象再用到这块内存,就造成了内存访问违例,产生异常。有个经典的 C++ 笔试题,让我们实现 String 类的相关函数,其主要目的就是用来考察对深拷贝与浅拷贝的理解的。题目中给出 String类的声明:

	class String{ public:     String();     String(const String & str);     String(const char* str);     String& operator=(String str);     char* c_str() const;     ~String();     int size() const; private:     char* data; }; 让写出上述几个函数的内部实现。这些函数的实现代码如下:

	//普通构造函数   String::String(const char *str) {   if (str == NULL)   {     m_data = new char[1];// 得分点:对空字符串自动申请存放结束标志''的,加分点:对m_data加NULL判断       *m_data = '';   }   else   {     int length = strlen(str);     m_data = new char[length + 1];// 若能加 NULL 判断则更好     strcpy(m_data, str);   } }     // String的析构函数   String::~String(void) {   delete[] m_data; // 或delete m_data;   }     //拷贝构造函数   String::String(const String &other)// 得分点:输入参数为const型   {        int length = strlen(other.m_data);   m_data = new char[length + 1];// 若能加 NULL 判断则更好     strcpy(m_data, other.m_data); }     //赋值函数   String & String::operator = (const String &other) // 得分点:输入参数为const型   {   if (this == &other)//得分点:检查自赋值       return *this;    if (m_data)       delete[] m_data;//得分点:释放原有的内存资源     int length = strlen(other.m_data);   m_data = new char[length + 1];//加分点:对m_data加NULL判断     strcpy(m_data, other.m_data);   return *this;//得分点:返回本对象的引用     }

	

	
		 
  审核编辑:汤梓红
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分