当前位置:编程学习 > C/C++ >>

C++编程调试秘笈----一些杂项

1、避免编写拷贝构造函数和赋值操作符,如果默认版本并不适用,可以考虑把拷贝构造函数和赋值操作符声明为私有,禁止类实例的复制
2、避免在析构函数中编写代码
需要析构函数的原因可能有好几个:
a、在基类中,可能需要声明虚拟析构函数,这样就可以使用一个指向基类的指针指向一个派生类的实例
b、在派生类中,并不需要把析构函数声明为虚拟函数,但是为了增加可读性,也可以这样做
c、可能需要声明析构函数并不抛出任何异常
下来讨论一下为什么析构函数应该是空的:
[cpp]  
class Student  
{  
public:  
    Student  
    {  
        if (!number_)  
        {  
            number_ = new int(age);  
        }  
    }  
  
    ~Student()  
    {  
        if (number_)  
        {  
            delete number_;  
        }  
    }  
  
private:  
    int* number_;  
};  
 
看看这个Student类,因为他在构造函数中获取了一些资源,所以需要在析构函数中释放掉。
但是现在问题来了,这个类的设计----每次添加一个表示个人描述的新元素(例如number_)时,都需要在析构函数中添加对应的清理代码,这就违背了“不要迫使程序员记住某些事情”的原则;另一个问题是,缺少安全检查,首先你的number_可能是要必须大于0的,这样还得在new的前面进行if判断,另外一个极端的情况:
[cpp] 
#include "stdafx.h"  
#include "iostream"  
#include "string"  
  
class A  
{  
public:  
    A() { std::cout << "Creating A" << std::endl; }  
    ~A(){ std::cout << "Destroying A" << std::endl; }  
};  
  
class B  
{  
public:  
    B() { std::cout << "Creating B" << std::endl; }  
    ~B(){ std::cout << "Destroying B" << std::endl; }  
};  
  
class C : public A  
{  
public:  
    C()   
    {   
        std::cout << "Creating C" << std::endl;   
        throw "Don't like C";  
    }  
    ~C(){ std::cout << "Destroying C" << std::endl; }  
private:  
    B b_;  
};  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    try  
    {  
        C c;  
    }  
    catch (...)  
    {  
        std::cout << "Caught an exception" << std::endl;  
    }  
  
    return 0;  
}  
 
因为在C中throw了一个异常,所以导致class C的析构函数没有被执行,这就导致了一些问题的产生
改成下面的形式设计就要好得多:
[cpp] 
class Student  
{  
public:  
    Student(const int number, const char* name)  
    {  
        SCPP_ASSERT(number > 0, "number must be large 0");  
        number_ = new int(number);  
        SCPP_ASSERT(name != NULL, "name must be not null");  
        name_ = new std::string(name);  
    }  
  
    ~Student()  
    {  
    }  
private:  
    Student(const Student& student);  
    Student& operator = (const Student&);  
  
    scpp::ScopedPtr<int> number_;  
    scpp::ScopedPtr<std::string> name_;  
};  
 
即使第二个安全检查抛出异常,执行name_的智能指针的析构函数仍然会被调用,并执行他的清理工作。另一个附带的好处是,我们并不需要操心把这些智能指针初始化为NULL,这是自动完成的。因此,可以看到构造函数抛出异常是一种潜在的危险行为;对应的析构函数将不会被调用,因此可能会存在问题,除非析构函数是空函数。
3、编写一致的比较操作符
常用的操作符有< > <= >= == !=,站在C++的角度,这些操作可以写成6个相互完全独立的函数,但是他们相互之间又有联系,比如x1>x2成立,x2<x1和x1>=x2也是成立的。所以,我们需要一些步骤来帮我实现这个目标(往之前的scpp_types.h上面添加):
[cpp]  
#ifndef __SCCP_TYPES_H__  
#define __SCCP_TYPES_H__  
  
#include <ostream>  
#include "scpp_assert.h"  
  
template <typename T>  
class TNumber  
{  
public:  
    TNumber(const T& x =0 )  
        :data_(x)  
    {  
  
    }  
    operator T() const   
    {  
        return data_;  
    }  
  
    TNumber &operator = (const T& x)  
    {  
        data_ = x;  
        return *this;  
    }  
  
    TNumber operator ++(int)  
    {  
        TNumber<T> copy(*this);  
        ++data_;  
        return copy;  
    }  
  
    TNumber operator ++()  
    {  
        ++data_;  
        return *this;  
    }  
  
    TNumber& operator += (T x)  
    {  
        data_ += x;  
        return *this;  
    }  
  
    TNumber& operator -= (T x)  
    {  
        data_ -= x;  
        return *this;  
    }  
  
    TNumber& operator *= (T x)
补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,