例如,假设我们希望确保“表现无限精度”的数值对象只能诞生于heap之中:
class UPNumber
{
public:
UPNumber();
UPNumber(int initValue);
UPNumber(double initValue);
UPNumber(const UPNumber& rhs);
private:
~UPNumber();//dtor位于private内
};
//Clients于是应该这么写:
UPNumber n; //错误!(虽然合法,但当n的dtor被隐式调用,就不合法了)
UPNumber* p = new UPNumber; //良好
...
delete p; //错误!企图调用private destructor
p->destroy(); //良好
class UPNumber{...}; //将dtor或ctor声明为private
class NonNegativeUPNumber:public UPNumber{...};//错误!dtor或ctors无法通过编译
class Asset
{
private:
UPNumber value; //错误!dtor或ctors无法通过编译
...
};
解决:
class UPNumber{...}; //注意将dtor声明为protected
class NonNegativeUPNumber:public UPNumber{...}; //derived classes可以调用protected members
class Asset
{
public:
Asset(int initValue);
~Asset();
...
private:
UPNumber* value;
};
Asset::Asset(int initvalue):value(new UPNumber(initValue))
{
...
}
Asset::~Asset()
{
value->destroy();
}
abstract mixin base class(抽象混合式基类):
- abstract base class是一个不能被实例化的基类;
- mixin class则提供一组定义完好的能力,能够与其派生类所可能提供的其他任何能力兼容。
我们可以形成一个所谓的抽象混合式基类,用来为派生类提供“判断某指针是否以operator new
分配出来的能力:
class HeapTracked //mixin class;追踪并记录被operator new返回的指针
{
public:
class MissingAddress{};
virtual ~HeapTracked() = 0;
static void* operator new(size_t size);//负责分配内存内存并将条目加入list内
static void operator delete(void* ptr);//负责释放内存并从list身上移除条目
bool isOnHeap() const; //决定某对象的地址是否在list内
private:
typedef const void* RawAddress;
static list<RawAddress> addresses; //list记录所有由operator new返回的指针
};
//HeapTracked的完整实现内容:
list<RawAddress> HeapTracked::addresses;
HeapTracked::~HeapTracked{}
void* HeapTracked::operator new(size_t size)
{
void* memPtr = ::operator new(size);
addresses.push_back(memPtr);
return memPtr;
}
void HeapTracked::operator delete(void* ptr)
{
list<RawAddress>::iterator it =
find(addresses.begin(),addresses.end(),ptr);
if(it != addresses.end())
{
address.erase(it);
::operator delete(ptr);
}else
{
throw MissingAddress();
}
}
bool HeapTracked::isOnHeap() const
{
//取得一个指针,指向*this所占内存的起始处
const void* rawAddress = dynamic_cast<const void*>(this);
list<RawAddress>::iterator it =
find(address.begin(),address.end(),rawAddress);
return it != address.end();
}
一般而言有3种可能:
- 对象被直接实例化;
- 对象被实例化为派生类对象内的“基类”成分;
- 对象被内嵌于其他对象之中。
欲阻止clients直接将对象实例化于heap之中,你可以让client无法调用new:
定义operator new
,将其声明为private
。
如果不希望clients将UPNumber对象产生于堆内:
class UPNumber
{
private:
static void* operator new(size_t size);
static void operator delete(void* ptr);
...
};
如果要禁止产生“UPNumer对象所组成的数组”,可以将operator new[]
和operator delete[]
声明为private。
将operator new声明为private,往往也会妨碍UNumber对象被实例化为heap-based derived class objects的“base class成分”,因为operator new和operator delete会被继承,所以如果这些函数不在derived class声明为public,derived class继承的便是base(s)所声明的private版本:
class UPNumber{...};
class NonNegativeUPNumber: //假设此class未声明operator new
public UPNumber
{
...
};
NonNegativeUPNumber n1;
static NonNegativeUPNumber n2;
NonNegativeUPNumber* p = new NonNegativeUPNumber;//错误!企图调用private operator new
如果derived class声明一个属于自己的operator new(且为public),类似于,当我们企图分配一个“内含UPNumber对象”的对象,“UNPumber的operator new乃为private“不会带来什么影响:
class Asset
{
public:
Asset(int initValue);
...
private:
UPNumber value;
};
Asset* pa = new Asset(100); //没问题,调用的是
//Asset::operator new或
//::operator new而非UPNumber::operator new
我们曾经希望“如果一个UPNumber对象被构造于heap以外,那么就在UPNumber constructor内抛出异常”,这次我们希望“如果对象被产生于heap内的话,就抛出一个异常”。然而,就像没有任何根据移植性的做法可以判断某地址是否位于heap内一样,我们也没有根据移植性的做法可以判断它是否不在heap内。