`
kofsky
  • 浏览: 201772 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

最近遇到的C++问题小结

阅读更多

    最近在将柑橘溃疡病系统由MATLAB平台迁移至VC平台,本可以通过其他工具来完成,但想到自己C++尤其磋,便想借此机会学习一下C++。于是下定决定核心代码全部重写。现在已经完成了一小部分的工作,包括核心类矩阵与向量的封装,并完成了部分特征提取的算法。尽管以前看过一些C++的书籍,也在VC平台上写过一些程序,但以前都是与杨世泉合作,他做的设计;只有这一次是,自己设计并实现之。

    这几个星期的开发遇到了许多的问题,设计方面的,语言方面的,其中细微,唯有亲历,才能体会,记录下来当作经验积累吧。

1、类型转换问题
      由高精度的数据转换成低精度的数据会产生数据丢失;低精度转换成高精度就不会有问题。
比如float转换成int会产生数据丢失,int转换成float就不会有问题。
    但如果将一个int数据a,先将其转换成float,再转换成int,会不会与原始的数据项等呢?如下例:

int a=5;
float b=a;
cout
<<a-(int)b<<endl;

测试一下,输出为0。转换成float再转换成int与原始数据是相等的。

int a=2523456789;
float b=a;
cout
<<a-(int)b<<endl;

再测一下,输出为 21!不相等了!把整形数往float上换一下,再换回来,就不等了!
咨询了一下高人。原因在于:
数据转换时的信息损失包括两种,一种是总值损失,一种是精度损失。
由int转换至float不可能再量上损失,但可能损失精度。
int->float->int,转换后,最低位有可能不同。因此,一个很大的数据经两次转换后就有很微小的差异了。

2、无符号数做索引产生的问题

typedef unsigned char BYTE;
typedef unsigned 
long DWORD;
for( DWORD i = 5; i >=0; i--)
{
//死循环,因为i永远不可能小于0
}

同理,如下也是死循环:

for( BYTE i = 5; i >=0; i--)
{
//...
}


3、const 成员函数 调用 非const成员函数
类 IVector 的两个函数:

   /** *//**
  * 计算向量的均值
  
*/

 
double mean();               // 非 const 成员函数
 /** *//**
  * 计算向量的方差
  
*/

 
double variance() const// const 成员函数

计算方差 variance 的函数中,需要调用 mean 函数计算向量的均值,调用处编译错误:

 error C2662: 'mean' : cannot convert 'this' pointer from 'const class IVector' to 'class IVector &'

   试图对一个const类型的类对象调用非const型的成员函数(mean)。  
   对于const型的成员函数,编译器会将其引用的任何成员变量标记为const类型——虽然你在类的声明中该成员变量可能不是,从而导致此错误。  
  解决方法1:将 mean 函数申明为 const 成员函数
  解决方法2:将 variance函数去掉 const 申明

  《高质量C++-C编程指南》:任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。

4、构造函数中调用构造函数时,有一个析构函数被调用的过程
类 A 构造函数 1:

IVector::IVector( DWORD _size,double defaultValue = 0.0 )
{
//...

 类 A 构造函数 2:

IVector::IVector( ... )
{
     IVector( 
1,0 );//调用构造函数1
}

构造函数 2在 调用构造函数1 后会马上调用析构函数
因此 该构造函数会完全不起作用。
这与 Java 不同,Java类的构造函数是完全可以相互调用的。
   
  
5、C++ 的 const 与 Java 的 final
在修饰变量时,
如果变量是基本类型,那么两者是完全一样的  
但是如果变量是对象,那就不同了  
const是指对象不可改变;而final表示对象的句柄(对象的引用)是不可改变的   
C++:

void g( const IVector vec)
{
 vec._size
=5;//错误! error C2166: l-value specifies const object
}

Java:

public void f( final IVector vec)
 
...{
    vec._size 
= 5;//OK
 }

C++中,变量vec是IVector对象本身,vec被申明为const后,则其不可修改;
Java中,变量vec保存的IVector对象的引用,并非对象本身。 vec被申明为final后,引用不可修改,但对象本身却是可以修改的。
 
6  内存越界访问造成数据删除时发生错误
边缘检测的模板类IEdgeMask,其有三个成员变量:

 BYTE _height;  
 BYTE _width;   
 
double** _mask; 

构造模板函数的时候

 _height = 3;
    _width 
= 2;//此处本应为3,但误写为2
    _mask = new double*[ _height ];
    
for ( BYTE i=0; i<_height; i++ )
    {
        _mask[i] 
= new double[ _width ];
    }

    _mask[
0][0=  0.16666666667;
    _mask[
0][1=  0.0000;
    _mask[
0][2= -0.16666666667// 越界访问数据
    _mask[1][0=  0.16666666667;
    _mask[
1][1=  0.0000;
    _mask[
1][2= -0.16666666667// 越界访问数据
    _mask[2][0=  0.16666666667;
    _mask[
2][1=  0.0000;
    _mask[
2][2= -0.16666666667// 越界访问数据


  能够越界对数据进行访问,执行对应的程序也没有任何问题。当函数体离开对象,执行对象的析构函数时,就出现问题了,无法删除数据成员_mask!

 for ( BYTE i=0; i<_height; i++ )
 {
  
if ( _mask[i] != NULL )
  {
   delete [] _mask[i];
//此处报出错误~
   _mask[i] = NULL;
  }
 }

 
if( _mask != NULL )
  delete []  _mask;

 _mask 
= NULL;
 _height 
= 0;
 _width 
= 0;
   

调试过程中会发现,所有的数据都是存在的。但就是无法删除该区域。
    内存越界访问造成的后果非常严重。它造成的后果是随机的,表现出来的症状和时机也是随机的,调试起来非常困难。需谨慎谨慎再谨慎。

7 永远的话题:传值、传引用
 细节就不写了。只记录几个点。
 传引用的场景:
  a、需要改变实参的值
  b、向主调函数返回额外的结果
  c、向函数传递大型对象
比如函数重载中用到的传引用:

 /**
  * 操作符重载 * : 矩阵与矩阵点乘
  
*/
 IMatrix 
& operator*const IMatrix &other );


参数传递时传引用时为了函数传递大型对象,减少不必要的对象拷贝
返回值传引用时为了实现链式表达式

C++传值,传对象本身的拷贝
Java传值,传对象引用的拷贝

 C++ 遇到问题最严重的并不是传说中的内存泄漏,而是越界访问、指针运算、临时指针这几个问题,错误隐藏的很深而不容易发现,调试过程真是太郁闷老。


附带印象中 Java 常出现的Exception:
NullPointerException
NumberFormatException
NoClassDefFoundError
ClassCastException
IOException
FileNotFoundException
SQLException
JDBCException
HibernateException
LazyException
SocketException
ServletException
  

分享到:
评论

相关推荐

    C++标准库中文件流读取操作小结

    ### C++标准库中文件流读取操作小结 在C++编程语言中,文件流是处理文件输入输出的一种常用方式。本文将详细介绍C++标准库中与文件流读取相关的几个重要知识点。 #### 一、`ifstream`类及其基本用法 在C++中,`...

    c和c++的一些小结

    - 及时查阅文档和参考书籍,遇到问题不要害怕寻求帮助,社区论坛和在线教程是很好的资源。 4. **常见误区与注意点**: - C++中的动态内存管理(如new和delete)需要谨慎处理,避免内存泄漏。 - C++中的构造函数...

    Accerlerated-C++中文版习题答案(含书pdf)

    《Accelerated C++》是一本深受C++程序员喜爱的经典教程,它强调实践和理解编程概念,而非仅仅记忆语法。本书的中文版习题答案包含了从第1章到第16章的所有练习,提供了详尽的解答,对于学习者来说是宝贵的参考资料...

    课程学习中c++ 的一点小结

    - **功能**:从给定的字符串`str`的起始位置开始计数,直到遇到第一个空字符`\0`为止。 - **返回值**:返回字符串中字符的数量(不包括`\0`)。 **示例:** ```cpp char str[1000]; strcpy(str, "hello world"); ...

    java/c++区别

    此文档旨在全面对比Java和C++,整合了网络上的众多资源,旨在帮助读者理解和解决实际编程中遇到的问题。尽管它可能不如专门的书籍详细,但仍提供了丰富的对比信息。 (二) 个人学习感受 作者从自身经验出发,指出...

    c++学籍管理系统 课程设计报告

    5. **错误处理**:添加异常处理机制,保证程序在遇到错误时能给出友好的提示。 6. **测试与调试**:进行全面的单元测试和集成测试,确保每个功能都能正常工作。 ### 3. 功能模块与流程图 系统主要包括以下几个模块...

    c++进阶知识小结(适用对c或c++有一定了解的人).docx

    C++是一种强大的面向对象编程语言,对于已经对C或C++有一定了解的人来说,深入学习C++的进阶知识是提升编程技能的关键。以下是一些重要的C++知识点的详细解释: 1. **换行符**:在C++中,`'\n'`是一个转义字符,...

    C++大作业记录

    最后,在个人小结中,我们需要总结设计过程中遇到的问题及解决方法,并且讨论尚未解决的问题及考虑应对的策略。这意味着我们需要总结和反思设计和实现程序的经验,并且使用它们来提高自己的编程能力。 本篇大作业...

    c++进阶知识小结(适用对c或c++有一定了解的人).pdf

    - `scanf`函数在遇到空格时会停止读取,如果需要读取包含空格的字符串,可以使用`gets`函数,它会读取到遇到`\n`或`\0`为止。 9. **条件运算符**:条件运算符`表达式1 ? 表达式2 : 表达式3`,如果`表达式1`为真,...

    C++基础与能力提升

    - **章节优化**: 新增“小结”和“术语”等章节,有助于读者更好地掌握每章的核心内容。 - **学习辅助手段**: - **术语标识**: 重要术语以黑体显示,已熟悉的重要术语以楷体表示。 - **语言特征标注**: 使用特殊...

    C++实验报告——个人银行账户管理系统.docx

    7. **异常处理**:为了处理可能的错误情况,如取款金额超过余额,实验中添加了异常处理机制,确保程序在遇到问题时能够正常运行,而不是崩溃。 8. **文件操作**:使用标准模板库(STL)中的文件流(fstream)进行...

    谭浩强C++程序设计 pdf版

    #### 小结 谭浩强的《C++程序设计》一书全面介绍了C++语言的基础知识及面向过程和面向对象的程序设计方法。通过本书的学习,读者可以系统地掌握C++语言的基本概念、语法结构以及高级特性,为进一步深入学习打下坚实...

    C++与matlab混合编译流程与问题分析

    本文将针对混合编译过程中遇到的问题进行小结,包括混合编译的处理流程、中间可能出现的问题以及这些问题的原因

    Dev-C++ 编译多文件程序的方法&安装使用教程

    "Dev-C++ 编译多文件程序的方法&安装使用教程" ...小结 在本教程中,我们讨论了如何使用 Dev-C++ 编译多文件程序,并提供了一个详细的安装和使用教程。希望这些信息能够帮助开发者更好地使用 Dev-C++。

    CPP.rar_c++错误心得_心得 C++

    总结文档"C++小结.doc"可能涵盖了上述部分或全部知识点,详细分析了C++编程中常见的错误和解决方法,是一份非常有价值的参考资料。通过学习和实践,我们可以不断提升C++编程技能,避免常见错误,编写出更高效、更...

    C#调用C++版本dll时的类型转换需要注意的问题小结

    C#对于C++的dll引用时,经常会遇到类型转换和struct的转换 1. C++ 里的Char类型是1 个字节,c#里的Char是两个字节,不可以对应使用;可使用c#里的byte对应 2. structType temp = (structType)Marshal.PtrToStructure...

    c c++ 动态单链表代码

    ##### 小结 本篇代码示例完整地演示了如何使用C/C++实现动态单链表的基本操作,包括节点的创建、链接以及链表的遍历。这种数据结构非常适合于需要动态扩展或缩减的情况,例如在实际应用中管理员工信息列表等场景。...

Global site tag (gtag.js) - Google Analytics