Loading

C++容易犯的编程错误

本文内容主要来自微信公众号:C语言与CPP编程,后续会根据学习内容陆续更新

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

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

class shape
{
    virtual Draw();
    //...
    static int nLevel;
}
  1. 函数参数的默认值写到函数实现中

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

bool CreateConf( const string& strConfName, const bool bAudio = false );
// 在函数实现处的参数中不用添加默认值:
bool CreateConf( const string& strConfName, const bool bAudio/* = false*/ )
{
    // ...
}
  1. 类的结尾处忘记添加 ";" 分号
class Shape
{
    // ...
};
  1. 函数中返回了一个局部变量的地址或者引用
    在函数中返回了一个局部变量的地址或者引用,而这个局部变量在函数结束时其生命周期就结束了,内存就被释放了。当外部访问到该变量的内存,会触发内存访问违例的异常,因为该变量的内存已经释放了。比如如下的错误代码:
char* GetResult()
{
    char chResult[100] = { 0 };
    // ...
    return chResult;
}
  1. 父类中的接口没有声明为 virtual 函数,导致多态没有生效

要实现多态下的函数调用,父类的相关接口必须声明为 virtual。

class Shape()
{
    // ...
    virtual void Draw();
    // ...
};
  1. 该使用双指针的地方,却使用了单指针

有时我们需要调用一个接口去获取某些数据,接口中将数据拷贝到传入的参数对应的内存中,此时设计参数时会传入指针或引用。我们在调用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( CodecInfo** pInfo)  // 此处的参数类型使用双指针
{
    memcpy(*pInfo, m_codecInfo, sizeof(CodecInfo));
}
  1. 应该使用深拷贝,却使用了浅拷贝

本来应该要进行深拷贝的,却使用了浅拷贝(直接赋值),导致另个不同生命周期的 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];// 得分点:对空字符串自动申请存放结束标志'\0'的,加分点:对m_data加NULL判断  
        *m_data = '\0';
    }
    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;  //得分点:返回本对象的引用    
}

参考文献:

posted @ 2022-04-11 09:30  锦瑟,无端  阅读(101)  评论(0编辑  收藏  举报