当前位置: 首页 > news >正文

【C++ techniques】要求/禁止/判断—对象产生于堆中

  • 有时候我们想让某种对象具有“自杀”的能力,所以我们必须要求对象存在堆中,以便我们调用delete this;
  • 另一些时候,我们要求拥有某种确定性,保证某一些类型绝不会发生内存泄漏,原因是没有任何一个该类型的对象从堆中分配出来;堆空间非常宝贵,所以我们要对象全为栈对象,必须禁止对象产生于堆中。

要求对象产生于堆中(Heap-Based Objects)

  • 为限制对象产生于heap,我们需阻止clients不得使用new以外的方法产生对象;
  • non-heap objects会在其定义点自动构造,并在其寿命结束时自动析构,所以只需让那些被隐式调用的构造函数和析构函数不合法;
  • 比较好的方法是让destructor成为private,而constructors仍为public;
  • 如此一来,你可以导入一个pseudo destructor函数,用来调用真正的destructor。Clients则调用pseudo-destructor来销毁它们产生的对象。

例如,假设我们希望确保“表现无限精度”的数值对象只能诞生于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(); //良好
  • 只要限制destructor和constructors的运用,便可阻止non-heap objects的诞生,但是,它妨碍了继承和内含:
class UPNumber{...};	//将dtor或ctor声明为private
class NonNegativeUPNumber:public UPNumber{...};//错误!dtor或ctors无法通过编译class Asset
{
private:UPNumber value;		//错误!dtor或ctors无法通过编译...
};

解决:

  1. 令UPNumber的destructor成为protected(并仍保持其constructors为public),便可以解决继承问题;
  2. “必须内含UPNumber对象”之classes可以修改为“内含一个指针,指向UPNumer”对象:
class UPNumber{...};	//注意将dtor声明为protected
class NonNegativeUPNumber:public UPNumber{...}; //derived classes可以调用protected membersclass 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种可能:

  1. 对象被直接实例化;
  2. 对象被实例化为派生类对象内的“基类”成分;
  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 newpublic 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内。

http://www.lryc.cn/news/183989.html

相关文章:

  • 吃鸡高手亲授:玩转绝地求生,分享顶级游戏干货!
  • Vue中如何进行自定义图表与可视化图形设计
  • 学信息系统项目管理师第4版系列19_质量管理
  • react库的基础学习
  • FFmpeg 基础模块:容器相关的 API 操作
  • SpringMVC+统一表现层返回值+异常处理器
  • 2023年地理信息系统与遥感专业就业前景与升学高校排名选择
  • 第五章:最新版零基础学习 PYTHON 教程—Python 字符串操作指南(第二节 - Python 字符串—Python 字符串 len()的语法)
  • ubuntu22.04使用共享文件设置
  • pycharm配置python3.8版本专门用于undecteded_chromedriver测试
  • 基于SpringBoot的民宿在线预定平台
  • CTFHUB SSRF
  • FreeRTOS入门教程(队列详细使用示例)
  • 【Kafka专题】Kafka收发消息核心参数详解
  • matlab 使用激光雷达检测、分类和跟踪车辆
  • 代码随想录训练营二刷第四十八天 | 139.单词拆分 背包问题总结
  • 【数据挖掘】2017年 Quiz 1-3 整理 带答案
  • 吃鸡高手必备工具大揭秘!提高战斗力,分享干货,一站满足!
  • 集群化环境前置准备
  • nodejs开发环境搭建
  • C语言qsort函数
  • 如何使用 Hotshot 通过文字生成 GIF 动画
  • 吃鸡高手必备!这些技巧帮你提高战斗力!
  • XV6 操作系统实验
  • leetcode - 双周赛114
  • 【LeetCode刷题笔记】双指针
  • 互联网Java工程师面试题·Memcached 篇·第二弹
  • 特斯拉被称为自动驾驶领域的苹果
  • stm32之HAL库操作PAJ75620
  • 医学影像归档与通讯系统(PACS)系统源码 PACS三维图像后处理技术