您的位置:首页 > 编程语言

C++编程思想第二版第二卷读书笔记1——编写健壮的系统

2009-04-19 16:59 471 查看
try{

throw 47;//throw a exception,可以是内建类型
throw t; //也可以是自定义的异常类的对象。
} catch(int i){
//捕获47
}catch(T t){ //父类可以捕获子类异常
//捕获t
}catch(…){//捕获任意异常
throw;//抛出空以释放资源,它不会再引起此catch块后与此try块匹配的异常捕获
}
每次只有第一个匹配的异常被捕获(因为同一时间点只可能抛出一个异常),且异常抛出位置所在块的资源将全部被回收。当异常处理块中再次抛出异常,将导致系统调用terminate()函数,可以使用#include<exception>中的set_terminate(函数名)自定义terminate()函数。
当在构造函数中抛出异常时,因为构造函数没有完成,所以系统不会调用期析构函数,此时需要手动清理已经构造的部分内容所占据的资源。这里有两招可以保证资源的释放:
1. 在构造函数内部捕获异常(这样就能正常调用析构函数了)
2. 使用一个<模板>类进行资源分配与回收。
template<class T, int sz = 1> class PWrap {
T* ptr;
public:
class RangeError {}; // Exception class
PWrap() {
ptr = new T[sz];
}
~PWrap(){
delete[] ptr;
}
};//This method is called Resource Acquisition Is Initialization(RAII for short)
所有要在构造函数内分配的资源都用如上类包装,这样无论是否完成构造,所有资源的分配的构造函数都会完成(也就保证了调用析构函数释放资源),无论调用不调用析构函数都能保证释放该释放的资源。C++本身有这样一个类,在<memory>中auto_ptr<T>。
try块可以用于任意函数前(包括类的构造函数和main函数)。
标准异常类有logic_error和runtime_error及他们的子类在#include<stdexcept>(这个头文件本身包含<exception>)中。
异常规约(Exception specifications):函数声名后列出所有可能抛出的异常(void f() throw(A,B,C);)如果不列出(void f();)表明可以抛出任何异常,如果为空(void f() throw();)表明不允许抛出异常。在模板类中不要使用异常规约。
如果函数抛出了不允许抛出的异常,就会引发unexpected()函数(可以用set_unexpected()自定义),也在<exception>头文件中。
如果基类中public函数中抛出异常类A的对象,那么派生类中该函数的重写函数只有抛出A或者A的子类异常。
operator=应当使用下面模式:
1. 不是给自己赋值,如果是的话,go to 6。
{
2. 为指针数据成员分配新的空间
3. 将数据从旧的空间拷贝到新的空间
}//clone()函数的来源
4. 删除旧的空间
5. 更新对象状态:指派新的堆指针指向指针数据成员。
6. 返回 *this
The best advice for deciding when to use exceptions is to throw exceptions only when a function fails to meet its specification.
不使用异常的情况:
1. 同步事件
2. Not for benign error conditions(you have enough information to handle an error)
3. 流程控制
4. 不被迫使用的情况(如打开文件失败)
5. 没有使用异常的已有代码
使用异常的典型场合:
1. Fix the problem and retry the function that caused the exception.
2. Patch things up and continue without retrying the function.
3. Do whatever you can in the current context and rethrow the same exception to a higher context.
4. Do whatever you can in the current context and throw a different exception to a higher context.
5. Terminate the program.
6. Wrap functions (especially C library functions) that use ordinary error schemes so they produce exceptions instead.
7. Simplify. If your error handling scheme makes things more complicated, it is painful and annoying to use. Exceptions can be used to make error handling simpler and more effective.
8. Make your library and program safer. This is a short-term investment (for debugging) and a long-term investment (for application robustness).
自定义异常最好包装到自己的命名空间。
捕获异常类引用而不是值:
1. 减少复制造成的性能损失。
2. 避免upcasting的切割。
#include<cassert>宏assert(),返回当前情形文件名和行数,可以用编译器选项禁用它,编译器程序 –DNDEBUG myfile.cpp(全局性禁用,assert主要用于测试)。也可以在程序中#define NDEBUG
Testing + programming is faster than just programming.
在实现之前先写好测试类,测试类继承自一个抽象类。测试类应该做成一个测试套装Suite可以一次性测试整个工程),通过一个addTest()函数添加新的测试单元或addSuite()来添加新的测试套装。
#define TRACE(ARG) cout << #ARG << endl; ARG//跟踪宏,如:
TRACE(for(int i = 0; i < 100; i++))
TRACE( cout << i << endl;)
表示:
cout << "for(int i = 0; i < 100; i++)" << endl;
for(int i = 0; i < 100; i++)
cout << "cout << i << endl;" << endl;
cout << i << endl;
可以使用Trace file代替所有的cout输出,只要包含头文件Trace.h并#define TRACEON即可。
//: C03:Trace.h
// Creating a trace file.
#ifndef TRACE_H
#define TRACE_H
#include <fstream>

#ifdef TRACEON
std::ofstream TRACEFILE__("TRACE.OUT");
#define cout TRACEFILE__
#endif

#endif // TRACE_H ///:~
找到内存泄漏的好方法就是在#include列表最后一行(确保所有的内存分配都被监视)使用MemCheck.h头文件(它重新定义了new和new[]):
//: C02:MemCheck.h
#ifndef MEMCHECK_H
#define MEMCHECK_H
#include <cstddef> // For size_t

// Usurp the new operator (both scalar and array versions)
void* operator new(std::size_t, const char*, long);
void* operator new[](std::size_t, const char*, long);
#define new new (__FILE__, __LINE__)

extern bool traceFlag;
#define TRACE_ON() traceFlag = true
#define TRACE_OFF() traceFlag = false

extern bool activeFlag;
#define MEM_ON() activeFlag = true
#define MEM_OFF() activeFlag = false

#endif // MEMCHECK_H ///:~
它的实现文件:
//: C02:MemCheck.cpp {O}
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstddef>
using namespace std;
#undef new

// Global flags set by macros in MemCheck.h
bool traceFlag = true;
bool activeFlag = false;

namespace {

// Memory map entry type
struct Info {
void* ptr;
const char* file;
long line;
};

// Memory map data
const size_t MAXPTRS = 10000u;
Info memMap[MAXPTRS];
size_t nptrs = 0;

// Searches the map for an address
int findPtr(void* p) {
for(size_t i = 0; i < nptrs; ++i)
if(memMap[i].ptr == p)
return i;
return -1;
}

void delPtr(void* p) {
int pos = findPtr(p);
assert(pos >= 0);
// Remove pointer from map
for(size_t i = pos; i < nptrs-1; ++i)
memMap[i] = memMap[i+1];
--nptrs;
}

// Dummy type for static destructor
struct Sentinel {
~Sentinel() {
if(nptrs > 0) {
printf("Leaked memory at:/n");
for(size_t i = 0; i < nptrs; ++i)
printf("/t%p (file: %s, line %ld)/n",
memMap[i].ptr, memMap[i].file, memMap[i].line);
}
else
printf("No user memory leaks!/n");
}
};

// Static dummy object
Sentinel s;

} // End anonymous namespace

// Overload scalar new
void*
operator new(size_t siz, const char* file, long line) {
void* p = malloc(siz);
if(activeFlag) {
if(nptrs == MAXPTRS) {
printf("memory map too small (increase MAXPTRS)/n");
exit(1);
}
memMap[nptrs].ptr = p;
memMap[nptrs].file = file;
memMap[nptrs].line = line;
++nptrs;
}
if(traceFlag) {
printf("Allocated %u bytes at address %p ", siz, p);
printf("(file: %s, line: %ld)/n", file, line);
}
return p;
}

// Overload array new
void*
operator new[](size_t siz, const char* file, long line) {
return operator new(siz, file, line);
}

// Override scalar delete
void operator delete(void* p) {
if(findPtr(p) >= 0) {
free(p);
assert(nptrs > 0);
delPtr(p);
if(traceFlag)
printf("Deleted memory at address %p/n", p);
}
else if(!p && activeFlag)
printf("Attempt to delete unknown pointer: %p/n", p);
}

// Override array delete
void operator delete[](void* p) {
operator delete(p);
} ///:~
要使用时在main()中首先调用函数MEM_ON();。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: