1-5 清理

1.5 清理

1.5.1 引入

下面这个例子,演示了构造函数中抛出异常,析构函数不会被调用(因为构造没有完成)。同时演示了创建对象数组时抛出异常的情况。

#include <iostream>
using namespace std;

class Trace
{
static int count;
int objId;

public:
Trace()
{
objId = count++;
cout << "constructing Trace #" << objId << endl;
if (objId == 3)
throw 3;
}
~Trace()
{
cout << "destructing Trace #" << objId << endl;
}
};

int Trace::count = 0;

int main()
{
try
{
Trace n1;
Trace array[5]; //在构造array[2]时抛出异常,array[2]并没有完成构造,因此不会被析构
Trace n2;
}
catch(int i)
{
cout<<"catch "<<i<<endl;
}
return 0;
}
/*output:
constructing Trace #0
constructing Trace #1
constructing Trace #2
constructing Trace #3
destructing Trace #2
destructing Trace #1
destructing Trace #0
catch 3
*/

1.5.2 资源管理(important)

由于构造函数因异常而未完成,会不调用析构函数。因此会出现资源泄露的情况。下面是一个UseResource析构未调用,导致Cat析构也同样未能正常调用,出现悬挂指针的情况。

#include <iostream>
#include <cstddef>
using namespace std;

class Cat
{
public:
Cat() { cout << "Cat()" << endl; }
~Cat() { cout << "~Cat()" << endl; }
};

class Dog
{
public:
void *operator new(size_t sz)
{
cout << "allocating a dog." << endl;
throw 47;
}
void operator delete(void *p)
{
cout << "deallocating a dog." << endl;
::operator delete(p);
}
};

class UseSources
{
Cat *bp;
Dog *op;

public:
UseSources(int count = 1)
{
cout << "UserSources()" << endl;
bp = new Cat[count];
op = new Dog;
}
~UseSources()
{
cout << "~UseResource()" << endl;
delete[] bp;
delete op;
}
};

int main(int argc, const char** argv) {
try
{
UseSources ur(3);
}
catch(int)
{
cout<<"inside handler"<<endl;
}

return 0;
}
/*output:
UserSources()
Cat()
Cat()
Cat()
allocating a dog.
inside handler
*/

1.5.3 使所有事物都成为对象

1. 资源获得式初始化(RAII技术)(important)

该技术保证每一个对象的资源分配都具有原子性。方法是构建一个类模板,将指针嵌入这个类中。此例中,类是自定义的,但是C++提供了auto_ptr来减少工作量。

#include <iostream>
#include <cstddef>
using namespace std;

template <class T, int sz = 1>
class PWrap
{
T *ptr;

public:
class RangeError
{
};
PWrap()
{
ptr = new T[sz];
cout << "PWarp constructor" << endl;
}
~PWrap()
{
delete[] ptr;
cout << "PWarp destructor" << endl;
}
T &operator[](int i) throw(RangeError)
{
if (i >= 0 && i < sz)
return ptr[i];
throw RangeError();
}
};

class Cat
{
public:
Cat() { cout << "Cat()" << endl; }
~Cat() { cout << "~Cat()" << endl; }
void g() {}
};

class Dog
{
public:
void *operator new[](size_t)
{
cout << "Allocating a Dog" << endl;
throw 47;
}
void operator delete[](void *p)
{
cout << "Deallocating a Dog" << endl;
::operator delete[](p);
}
};

class UseResources
{
PWrap<Cat, 3> cats; //通过将指针嵌入这个PWrap<Cat, 3>类中
//即使下面PWrap<Dog>类里的Dog构造时抛出异常
//这个PWrap<Cat, 3>类的构造也正常完成了
//就避免了不调用这个PWrap<Cat, 3>类析构使Cat资源泄露的问题
PWrap<Dog> dog;

public:
UseResources() { cout << "UseResources()" << endl; }
~UseResources() { cout << "~UseResources()" << endl; }
void f() { cats[1].g(); }
};

int main()
{
try
{
UseResources ur;
}
catch (int)
{
cout << "inside handler" << endl;
}
catch (...)
{
cout << "inside catch(...)" << endl;
}
}

/*
Cat()
Cat()
Cat()
PWarp constructor
Allocating a Dog
~Cat()
~Cat()
~Cat()
PWarp destructor
inside handler
*/
2.使用auto_ptr代替自定义类

C++提供了封装指针的类来减少工作量。(本代码不重要)

#include <memory>
#include <iostream>
#include <cstddef>
using namespace std;

class TraceHeap
{
int i;

public:
static void *operator new(size_t sz)
{
void *p = ::operator new(sz);
cout << "Allocating TraceHeap object on the heap"
<< " at address " << p << endl;
return p;
}
static void operator delete(void *p)
{
cout << "Deleting TraceHeap object at address "
<< p << endl;
::operator delete(p);
}
TraceHeap(int i) : i(i)
{
}
int getVal() const { return i; }
};

int main(int argc, char const *argv[])
{
auto_ptr<TraceHeap> pMyObject(new TraceHeap(5));
cout << pMyObject->getVal() << endl;
return 0;
}

1.5.4 函数级的try块

C++允许在函数级就使用try块,将函数体作为try的内容。下面是一个构造函数使用函数级try块的例子。

#include <iostream>
using namespace std;

class Base
{
int i;

public:
class BaseExcept
{
};
Base(int i) : i(i) { throw BaseExcept(); }
};

class Derived : public Base
{
public:
class DerivedExcept
{
const char *msg;

public:
DerivedExcept(const char *msg) : msg(msg) {}
const char *what() const { return msg; }
};
Derived(int j)
try : Base(j)
{
cout << "This won't print" << endl;
}
catch (Base::BaseExcept &)
{
throw DerivedExcept("Base Subobject throw");
}
};

int main(int argc, char const *argv[])
{
try
{
Derived(3);
}
catch (Derived::DerivedExcept &d)
{
cout << d.what() << endl;
}

return 0;
}