13.如何做到要求或禁止在堆中产生你自定义的对象

来源:互联网 发布:淘宝聊天工具下载 编辑:程序博客网 时间:2024/04/30 12:04
如果你的程序要在嵌入式系统上工作,发生在嵌入式系统上的内存泄露是非常严重的,它的堆空间是非常珍贵的。有没有可能编写出来的代码要求或禁止在堆中产生对象呢。通常是可以的。
1.要求在堆中建立对象
意思就是我们必须调用new才能创建一个对象,非堆对象在定义它的地方被自动构造,在生存时间结束时自动被释放,所以只要禁止使用隐式构造函数和析构函数,就可以实现这种限制。
最简单的方法就是将构造函数和析构函数声明为private,私有的。但是这样做副作用太大。
我们只需要将析构函数设置成私有的就可以。然后定义一个伪析构函数,让他来访问真正的析构函数。
例如下面一个类:
class UPNumber {public:UPNumber();UPNumber(int initValue);UPNumber(double initValue);UPNumber(const UPNumber& rhs);// 伪析构函数 (一个const 成员函数, 因为// 即使是const对象也能被释放。)void destroy() const { delete this; }...private:~UPNumber();//析构函数};
使用时:
UPNumber n;//这里定义是合法的,但是当析构函数被隐世调用时就不合法了。
UPNumber *p=new UPNumber; //正确
delete p;//不正确,调用私有的析构函数
p->destroy();//正确,调用伪析构函数,删除对象本身。
另一种做法是将所有的构造函数都声明为private,然后用为构造函数返回创建的对象。但是这里有一个问题是,一个类经常有许多构造函数,你必须把这些构造函数都声明为private。包括拷贝构造函数,缺省构造函数等,不然编译器会自动替你生成这些函数,而编译器生成的这些函数都是public的。
虽然这样做是可以的,但是我们是需要付出代价的,我们将不能使用继承和包含。也就是说其他的类不能从这个类继承,也不能有这个类的成员。
经说过,这种方法也禁止了继承和包容(containment):
class UPNumber { ... }; // 声明析构函数或构造函数// 为privateclass NonNegativeUPNumber:public UPNumber { ... }; // 错误! 析构函数或//构造函数不能编译class Asset {private:UPNumber value;... // 错误! 析构函数或//构造函数不能编译};
通过把析构函数设置成protected,我们可以解决继承问题,需要包含UPNumber对象的类可以修改成包含指向UPNumber的指针:
class UPNumber { ... }; // 声明析构函数为protectedclass NonNegativeUPNumber:public UPNumber { ... }; // 现在正确了; 派生类// 能够访问// protected 成员class Asset {public:Asset(int initValue);~Asset();...private:UPNumber *value;};Asset::Asset(int initValue): value(new UPNumber(initValue)) // 正确{ ... }Asset::~Asset(){ value->destroy(); } // 也正确
2.禁止在堆中创建对象
通常建立一个对象有三种情况:
a.对象被直接实例化
b.对象作为派生类的基类,在派生类实例化时被实例化。
c.对象被嵌入到其他对象中。
要想实现不允许在堆中创建对象,我们应先考虑new操作符,因为每次都是调用new来创建堆中对象。实现禁止调用new就能实现我们的功能。由于new操作符是调用operator new函数来达到目的的。所以我们可以自己将这个函数声明为private。例如:
class UPNumber {
private:
static void *operator new(size_t size);
static void operator delete(void *ptr);
...
};
现在用户仅仅可以做允许它们做的事情:
UPNumber n1; // okay
static UPNumber n2; // also okay
UPNumber *p = new UPNumber; // error! attempt to call
// private operator new
如果你也想禁止UPNumber堆对象数组,可以把operator new[]和operator delete[]也声明为private。
有趣的是,把operator new声明为private经常会阻碍UPNumber对象做为一个位于堆中的派生类对象的基类被实例化。因为operator new和operator delete是自动继承的,如果operator new和operator delete没有在派生类中被声明为public(进行改写,overwrite),它们就会继承基类中private的版本,如下所示:
class UPNumber { ... }; // 同上class NonNegativeUPNumber: //假设这个类public UPNumber { //没有声明operator new...};NonNegativeUPNumber n1; // 正确static NonNegativeUPNumber n2; // 也正确NonNegativeUPNumber *p = // 错误! 试图调用new NonNegativeUPNumber; // private operator new
如果派生类声明它自己的operator new,当在堆中分配派生对象时,就会调用这个函数,于是得另找一种不同的方法来防止UPNumber基类的分配问题。UPNumber的operator new是private这一点,不会对包含UPNumber成员对象的对象的分配产生任何影响:
class Asset {public:Asset(int initValue);...private:UPNumber value;};Asset *pa = new Asset(100); // 正确, 调用// Asset::operator new 或// ::operator new, 不是// UPNumber::operator new


原创粉丝点击