C/C++编程:要求或禁止在堆中产生对象

1059 篇文章 283 订阅

要求在堆中产生对象

加入我们要求每个类必须在堆中建立对象。为了执行这个限制,你必须找到一种方法禁止调用new之外的其他手段建立对象。这很容易做到,non-heap-object在定义它的地方被自动构造,在生存期结束时被自动释放,所以只要禁止使用隐式构造函数和析构函数,就可以实现这种限制。

把这些调用变得不合法的一种最直接的方法是把构造函数和析构函数声明为private。这样做副作用太大,没有理由让这两个函数都是private。最好让析构函数private,构造函数public。你可以引进一个占用的伪析构函数,用来访问这真正的析构函数。客户端调用伪析构函数释放他们建立的对象。(WQ 加注:注意,异
常处理体系要求所有在栈中的对象的析构函数必须申明为公有!)

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; // 错误! 试图调用  private 析构函数 
p->destroy(); // 正确 

另一种方法是把全部的构造函数声明为private。这种方法的缺点是一个类经常有很多构造函数,类的作者必须记住把它们都声明为private。否则如果这些函数就会由编译器生成,构造函数包括拷贝构造函数,也包括缺省构造函数;编译期生成的函数总是public的。因此仅仅声明析构函数为private是很简单的,因为每个类仅有一个析构函数。

通过限制访问一个类的析构函数或者它的构造函数来阻止建立non-heap object。这种方法也禁止了继承和包容:

class UPNumber { ... }; // 声明析构函数或构造函数  为 private 
class NonNegativeUPNumber: public UPNumber { ... }; // 错误! 析构函数或 构造函数不能编译 
class Asset { 
private: 
 	UPNumber value;  // 错误! 析构函数或 构造函数不能编译 
 	... 
};

这些困难不是不能克服的。通过把 UPNumber 的析构函数声明为 protected(同时它的构造函数还保持 public)就可以解决继承的问题,需要包含 UPNumber 对象的类可以修改为包含指向 UPNumber 的指针

class UPNumber { ... }; // 声明析构函数为 protected 
class 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(); } // 也正确 

禁止堆对象

通常对象的建立有三种情况:

  • 对象被直接实例化
  • 对象作为派生类的基类被实例化
  • 对象被嵌入到其他对象内。

禁止用户直接实例化对象很简单,因为总是调用new来建立这种对象,你能够禁止用户调用new。你不能影响new操作符的可用性(这是内嵌于语言中),但是你能利用new操作符总是调用operator new函数这点来达到目的。你可以自己声明这个函数,而且你可以把它声明为 private。例如,如果你想不想让用户在堆中建立 UPNumber对象,你可以这样编写:

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的private operator new,不会对对包含 UPNumber 成员对象的对象的分配产生任何影响:

class Asset { 
public: 
 Asset(int initValue); 
 ... 
private: 
 UPNumber value; 
}; 
Asset *pa = new Asset(100); 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值