ed:
ABC();
ABC( const ABC& );
};
class D : publicABC
{
//…
};
void func1(ABC);
void func2(ABC&);
ABC a; //错误!默认构造函数是受保护的
D d; //OK
func1(d); //错误!复制构造函数是受保护的、
func2(d); //ok,不涉及复制构造函数
方法2:另一种使一个类成为抽象基类的方式是将该类的一个虚函数指定为纯虚函数。通常来说,析构函数是一个不错的选择:
class ABC{
public:
virtual ~ABC() = 0;
};
ABC::~ABC(){…}
注意,在这个例子中,很有必要为该纯虚函数提供一个实现,因为派生类的析构函数将会隐式地调用其基类的析构函数(注意,从一个派生类析构函数内部对一个基类析构函数的隐式调用,总是非虚拟的)。
方法3:受保护的析构函数和受保护的构造函数发挥的效果基本相同,不过前者的报错发生于对象离开作用域时或被显示销毁时,而不是在对象创建时。
class ABC{
protected:
~ABC();
public:
//…
};
void someFunc()
{
ABC a; //此时不会报错
ABC *P = new ABC; //此时不会报错
delete p; //错误!析构函数时受保护的
//错误!隐式调用a的析构函数
}
条款34禁止或强制使用堆分配
有时候,指明一些特定类的对象不应该被分配到堆上是个好主意。通常这是为了确保该对象的析构函数会得到调用。维护对本体对象的引用计数的句柄对象就属于这种对象。具有自动存储区的类的局部对象,其析构函数会被中调用,具有静态存储区的类的对象亦然。
指明对象不应该被分配到堆上的方法之一,是将其对内存分配定义不合法:
class NoHeap
{
public:
//….
protected:
void* operator new(size_t){return 0;}
void operator delete(void*){}
};
任何在堆上分配一个NoHeap对象的习惯性尝试,都会产生编译器错误。
NoHeap * nh =new NoHeap; //错误!NoHeap::operatornew是受保护的
//…
delete nh; //错误!NoHeap::operatordelete是受保护的
之所以给出operator new和operator的定义,是因为在一些平台上它们可能会被构造和析构函数调用。处于同样原因,我们将其声明为protected是因为它们可能被派生类的构造函数和析构函数隐式的调用。如果NoHeap不打算用作基类,那么这两个函数也可以声明为private。
同时,还要注意阻止堆上分配NoHeap对象的数组。在这种情况下,只要将array new和array delete声明为private且不予定义即可,类似于禁止复制操作的方式。
class NoHeap{
public:
//…
protected:
void* operator new(size_t ){return 0;}
void operatoe delete(void*){}
private:
void* operator new[](size_t);
void operator delete[](void*);
};
当然,在某些场合下,我们可能鼓励使用对分配,只需要将析构函数声明为private即可。
class OnHeap{
~OnHeap();
public:
void destroy()
{
delete this;
}
};
当对象的名字离开其作用域时,任何一个声明自动或静态OnHeap对象的尝试,都会产生一个隐式的析构函数调用:
OnHeap oh1; //错误!隐式调用私有析构函数
void aFunc()
{
OnHeap oh2;
//…
//错误!隐式调用oh2析构函数
}