C++ | 【03 异常】More Effective C++

本文探讨了C++中防止资源泄漏的方法,如使用析构函数和智能指针。在构造函数中确保资源正确分配和释放,以及异常处理的细节,如禁止异常信息传递到析构函数外。同时,解释了抛出异常与传递参数的区别,并强调了异常处理的系统开销和80-20准则在优化代码中的应用。
摘要由CSDN通过智能技术生成

索引

C++ | 【01 基础提议】More Effective C++🧡💛💚💙
C++ | 【02 运算符】More Effective C++🧡💛💚💙
C++ | 【03 异常】More Effective C++🧡💛💚💙
C++ | 【04 效率】More Effective C++🧡💛💚💙
C++ | 【05 技巧】More Effective C++🧡💛💚💙
C++ | 【06 杂项】More Effective C++🧡💛💚💙

- 如果一个函数通过设置一个状态变量或返回错误代码来表示一个异常状态,若没办法保证函数调用者将一定检测变量或测试错误
	代码,若程序继续运行,此时需要捕获异常,否则程序将会终止;
- 异常处理要能通知不可被忽略的异常状态,并需要在搜索栈空间,以便找到异常处理代码时,能确保局部对象的析构函数必须被
	调用;

10、使用析构函数防止资源泄漏

当函数内部异常new的对象,没有被delete,出现内存泄漏

一般能构建一个类似auto_ptr的类对new对象进行管理;

template<class T>
class  auto_ptr {
public:
    auto_ptr(T *p=0) : ptr(p) {}
    ~auto_ptr() { delete ptr; }
    
private:
    T *ptr;
};

11、在构造函数中防止资源泄漏

C++中删除空指针是安全的;

如何确保构造函数new对象,防止出现异常而导致资源泄漏

以下使用auto_ptr对new对象进行管理:
- 对象类型为const,故只能在构造初始化列表中初始化;
- 为了防止在初始化中new出现异常而导致资源泄漏,使用autoptr能够自动析构;
template<class T>
class  auto_ptr {
public:
    auto_ptr(T *p=0) : ptr(p) {}
    ~auto_ptr() { delete ptr; }

private:
    T *ptr;
};

class Image {
public:
    Image(const string& imageFilename);
};

class AudioClip {
public:
    AudioClip(const string& audioFilename);
};

class BookEntry {
public:
    BookEntry(const string& name,
              const string& imageFilename,
              const string& audioClipFilename)
              : m_name(name),
                m_imageFilename(imageFilename != "" ? new Image(imageFilename) : 0),
                m_audioClipFilename(audioClipFilename != "" ? new AudioClip(audioClipFilename) : 0){}

private:
    string m_name;
    const auto_ptr<Image> m_imageFilename;
    const auto_ptr<AudioClip> m_audioClipFilename;
};

12、禁止异常信息传递到析构函数外

当对象被删除时,即析构函数会被调用;
禁止异常信息传递到析构函数外:
	- 当一个析构函数中抛出异常时,会导致程序的控制权转移到该函数之外,后调用terminate,导致局部对象没有被释放;
	- 当异常抛出时,函数内部无法完成它所希望的所有事情;
Session:~Session() {
	try {
		// ...
	}catch(...) {
		// ...
	}
}

13、理解抛出一个异常与传递一个参数或调用一个虚函数间的差异

在异常中参数传递内部系统过程与函数参数传递时不同的:
	- 函数参数,程序的控制权最终函数会返回到函数的调用处;
	- 而抛出一个异常,控制权永远不会回到抛出异常的地方;
	- 异常对象在传递时总被拷贝,当传值方式被拷贝两次,对象传给函数不一定需要被拷贝;
	- 异常对象类型转换比后者要少;
	- 异常中允许传递一个临时对象到一个非const引用类型参数,函数中不允许;
	- 抛出异常运行速度比参数参数传递慢;
13.1 要求被作为异常抛出的对象必须被复制

由于当localw离开该函数后,即被析构了,且改拷贝动作时通过对应类拷贝构造来完成的;

class Widget {};
istream operator>>(istream& s, Widget& w) {
}

void passAndThrowWidget() {
    Widget localW;
    cin >> localW;
    // throw 		// 捕获当前异常
    throw localW;	// 对localw进行拷贝动作
}
使用throw来重新抛出当前异常,不会改变被传递出去的异常类型,效率高,不会生成新拷贝;
13.2 catch中参数传递方式
# 传值捕获
catch(Widget w)
> 会建立两个被抛出对象的拷贝:
> 	- 所有异常都必须建立的临时对象;
> 	- 临时对象拷贝到w中;

# 引用捕获
catch(Widget& w)
catch(const Widget& w)
> 会建立一个被抛出对象的拷贝;
13.3 异常匹配时可以进行类型转换
- 继续类与基类之间的转换;
	不要把处理基类的catch子句放在处理派生类异常的catch子句前面;
- 允许从一个类型化指针转变成无类型指针;
	const void* 能捕捉任意类型的指针;

14、通过引用捕获异常

以下传递方式,没有对象拷贝;
但前提时改对象需要为全局或堆上,不能为局部变量,否则当抛出时已经被释放;
-  使用该方法能够避开删除对象释放异常;
-  避开slicing异常对象;
- 减少异常对象需要被拷贝的数目;
void func() {
    static exception ex;
    
    throw &ex;
}

void func() {
    try {
        //...
    }catch(exception& ex) {
        // 
    }
}

15、审慎使用异常规格

- unexpected缺省下是调用terminate,而terminate缺省下是调用abort;
- 违反异常规格:
	当A调用B,B中抛出一个不在A异常规格内的异常;
	- 1)避免在带有类型参数内使用异常规格;
	- 2)若一个函数内调用其他没有异常规格的函数时,需去除这个函数的异常规格;
	- 3)处理系统本身抛出的异常;

第2点示例

typedef void(*CallBackPtr)(int a, int b, void* args);

class CallBack{
public:
    CallBack(CallBackPtr ptr, void* data)
    : m_ptr(ptr),
     m_data(data){}

     void makeCallBack(int a, int b) const throw() {
        m_ptr(a, b, m_data);
    }
private:
    CallBackPtr m_ptr;
    void* m_data;
};

此时func抛出的异常可能超过makeCallBack能捕捉的异常;

// 改进
typedef void(*CallBackPtr)(int a, int b, void* args) throw();

16、了解异常处理的系统开销

- 程序在运行时,处理异常需要做大量的记录信息,以至于能够确保跟踪到没一个步骤;
- try模块会使代码的尺寸增加5~10%且同等的下降速度;

17、牢记80 - 20准则

- 20%的代码使用了80%的程序资源(时间、内存、磁盘访问);
- 使用profiler正确的去分析程序,从而调高效率
作者 : Scott Meyers 译序、导读 : 侯捷 译序(侯捷) C++ 是一个难学易用的语言! C++ 的难学,不仅在其广博的语法,以及语法背後的语意,以及语意背後的深层思维,以及深层思维背後的物件模型;C++ 的难学,还在於它提供了四种不同(但相辅相成)的程式设计思维模式:procedural-based,object-based,object-oriented,generic paradigm。 世上没有白吃的午餐。又要有效率,又要有弹性,又要前瞻望远,又要回溯相容,又要能治大国,又要能烹小鲜,学习起来当然就不可能太简单。 在如此庞大复杂的机制下,万千使用者前仆後续的动力是:一旦学成,妙用无穷。C++ 相关书籍之多,车载斗量;如天上繁星,如过江之鲫。广博如四库全书者有之(The C++ Programming Language、C++ Primer),深奥如重山复水者有之(The Annotated C++ Reference Manual, Inside the C++ Object Model),细说历史者有之(The Design and Evolution of C++, Ruminations on C++),独沽一味者有之(Polymorphism in C++, Genericity in C++),独树一帜者有之(Design Patterns,Large Scale C++ Software Design, C++ FAQs),程式库大全有之(The C++ Standard Library),另辟蹊径者有之(Generic Programming and the STL),工程经验之累积亦有之(Effective C++, More Effective C++, Exceptional C++)。 这其中,「工程经验之累积」对已具C++ 相当基础的程式员而言,有著致命的吸引力与立竿见影的帮助。Scott Meyers 的Effective C++ 和More Effective C++ 是此类佼佼,Herb Sutter 的Exceptional C++ 则是後起之秀。 这类书籍的一个共通特色是轻薄短小,并且高密度地纳入作者浸淫於C++/OOP 领域多年而广泛的经验。它们不但开展读者的视野,也为读者提供各种C++/OOP 常见问题或易犯错误的解决模型。某些小范围主题诸如「在base classes 中使用virtual destructor」、「令operator= 传回*this 的reference」,可能在百科型C++ 语言书籍中亦曾概略提过,但此类书籍以深度探索的方式,让我们了解问题背後的成因、最佳的解法、以及其他可能的牵扯。至於大范围主题,例如smart pointers, reference counting, proxy classes,double dispatching, 基本上已属design patterns 的层级! 这些都是经验的累积和心血的结晶。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jxiepc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值