第五章,技术

27-要求(或禁止)对象产生于heap之中

要求对象产生于Heap之中的实现

当希望对象的存活时间可以灵活控制时,要求对象必须产生于heap之中;通过将析构函数private或者peotected来限制非heap上生成对象(禁止了自动销毁),提供销毁函数来调用析构函数。

class UPNumber{
public:
UPNumber();
UPNumber(int initValue);
UPNUmber(double initValue);
UPNumber(const UPNumber& rhs);

void destory() const { delete this; }
...

private:
~UPNumber();
};

UPNumber n; //错误!无法自动析构
UPNumber *p = new UPNUmber; //成功!
...
delete p; //错误!企图调用private destructor;
p->destory(); //成功!


//1、为了其他类能 【继承】 UPNumber,将析构函数改成protected
class UPNumber{
public:
UPNumber();
UPNumber(int initValue);
UPNUmber(double initValue);
UPNumber(const UPNumber& rhs);

void destory() const { delete this; }
...

protected:
~UPNumber();
};


class NonNegativeUPNumber : public UPNumber{ ... }; //成功!

//2、为了其他类能 【内含】 UPNumber对象,内含UPNumber指针即可
class Asset{
public:
Asset(int initValue) : value(new UPNUmber(initValue)){ ... }
~Asset(){ value->destory(); }
...

private:
UPNumber *value;
};

判断某个对象是否位于Heap内

可复用HeapTracked类

class HeapTracked{
public:
class MissingAddress{};
virtual ~HeapTracked() = 0;
static void *operator new(size_t size);//operator new/delete 无论在类内还是类外都是静态的,这里的static修饰是在明确这一点
static void operator delete(void *ptr);
bool isOnHeap() const;
private:
typedef const void* RawAddress;
static list<RawAddress> addresses;
};

//static class member的义务性定义
list<HeapTracked::RawAddress> HeapTracked::addresses;

//由于析构函数是纯虚函数,必须定义,否则派生类对象析构时会异常
HeapTracked::~HeapTracked(){}

void * HeapTracked::operator new (size_t size)
{
void *memPtr = ::operator new(size);
addresses.push_front(memPtr);
return memPtr;
}

void HeapTracked::operator delete(void *ptr)
{
list<RawAddress>::iterator it = find(addresses.begin(),addresses.end(),ptr);

if(it != addresses.end()){
addresses.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(addresses.begin(),addresses.end(),rawAddress);

return it != addresses.end();
}

HeapTracked使用示例

class Asset: public HeapTracked{
private:
UPNumber value;
...
};

void inventoryAsset(const Asset *ap)
{
if(ap->isOnHeap()){
// ap is a heap-based asset —— inventory it as such;
}
else{
// ap is a non-heap-based asset —— record it that way;
}
}

//TEST
Asset asset;
if (asset.isOnHeap()){
cout << "asset is on Heap" << "\n";
}
else{
cout << "asset is not on Heap" << "\n";
}

Asset *asset_ptr = new Asset();
if (asset_ptr->isOnHeap())
{
cout << "asset_ptr is on Heap" << "\n";
}
else {
cout << "asset_ptr is not on Heap" << "\n";
}

Asset *asset_ptr1 = &asset;
if (asset_ptr1->isOnHeap())
{
cout << "asset_ptr1 is on Heap" << "\n";
}
else {
cout << "asset_ptr1 is not on Heap" << "\n";
}

禁止对象产生于Heap之中

由于产生在Heap之中的对象总是以new产生出来的,可以将operator new和operator delete成为private来进行限制

class UPNumber{
private:
static void *operator new(size_t size);
static void operator delete(void *ptr);
static void *operator new[](size_t size);
static void operator delete[](void *ptr);
...
};

UPNumber n1; //成功
static UPNumber n2; //成功
UPNumber *p = new UPNUmber; //错误! 企图调用private operator new

//1、【继承】这样自然也会对派生类有一定限制作用
class NonNegativeUpNumber: public UPNumber{
...
};

NonNegativeUpNumber n1; //成功
static NonNegativeUpNumber n2; //成功
NonNegativeUpNumber *p = new NonNegativeUpNumber;//错误!企图调用private operator new(除非NonNegativeUpNumber自己重载operator new)

//2、【内嵌】没有影响
class Asset{
public:
Asset(int initValue);
...
private:
UPNumber value;
};
Asset *pa = new Asset(100); //没问题,调用的是Asset::operator new或::operator new而非UPNumber::operator new

关键提示: 判断某地址是否在Heap内是不具有移植性的! 因为不同系统的内存布局是不一样的。