如何判断一个C++c 判断对象是否被释放在堆栈上

如何判断一个对象是否属于某个类_百度知道&&&&&&&&&&&&&&&&&&
阅读排行榜
评论排行榜
昨天有人在QQ群里问到如何判断一个C++对象是否在堆栈上, 我在网上搜索了下, 搜到这个么一个CSDN的帖子&,可惜它也没有给出一个合适的答案。要解答这个问题,其实就是要知道的堆栈的起始地址, 而我们知道堆栈其实就是一段有相同属性的内存页面,而Windows也是有API让我们查询虚拟内存的页面分配情况的。所有我们可以通过VirtualQuery这个API来获取堆栈的起始地址,然后就可以得到答案了。BOOL&IsObjectOnStack(LPVOID&pObject){&&&&INT&nStackValue(0);&&&&MEMORY_BASIC_INFORMATION&mi&=&{0};&&&&DWORD&dwRet&=&VirtualQuery(&nStackValue,&&mi,&sizeof(mi));&&&&if(dwRet&&&0)&&&&{&&&&&&&&return&pObject&&=&mi.BaseAddress&&&&&&&&&&&&&&&&(DWORD)pObject&&&(DWORD)mi.BaseAddress&+&mi.RegionS&&&&}&&&&return&FALSE;}int&g_value&=&10;int&main(int&argc,&char*&argv[]){&&&&int&nStackValue&=&1;&&&&int*&p&=&new&int(10);&&&&&&&&BOOL&bStackValue&=&IsObjectOnStack(&g_value);&&//false&&&&&&&&bStackValue&=&IsObjectOnStack(&nStackValue);&//true&&&&&&&&bStackValue&=&IsObjectOnStack(p);&//false&&&&system("pause");&&&&return&0;}当然,我们知道每个线程都有自己的堆栈,所以上面的方法针对线程1查询线程1的堆栈对象是可行的,线程2查询线程2的堆栈对象页是可行的,但是线程1查询线程2的堆栈对象就不行了。所以多线程情况下,我们可以统计出所有的线程堆栈起始地址,然后统一判断。当然随着线程的建立和销毁,堆栈本身也是在不断变化的。我想了下,不知道判断对象是否在堆栈上在我们实际编程中有什么用,谁知道的话麻烦提示下。以上代码在Windows下测试通过,如果有不正确的地方,欢迎指正。
阅读(3002)
&re: 如何判断一个C++对象是否在堆栈上
学习了&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
搂主,怎么判断是不是堆对象?&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
@learner参照&&&&&&
&re: 如何判断一个C++对象是否在堆栈上[未登录]
不需要windows api也可以做到&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
楼主您好,因为这个错误不小,所以我还是觉得应该指出,只为共同学习&我们知道每个线程都有自己的堆栈,所以上面的方法针对线程1查询线程1的堆栈对象是可行的,线程2查询线程2的堆栈对象页是可行的,但是线程1查询线程2的堆栈对象就不行了&这句话错了,系统在在为每个进程初始化时创建一个默认堆,它是全局的,该进程中的所有线程共用这个全局堆,而系统会为每个线程创建一个栈,也就是说,如果没有自己手动创建堆(HeapCreate),那么一个进程只有一个堆,而栈是线程相关的.我想楼主本意是想说栈,而&堆栈&这个词用的多了,就疏忽了&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
@Duwen多谢指正,希望共同进步。恩,可能表达不是很清楚,我上面说的堆栈其实指的就是栈(Stack)。至于堆(Heap), 每个进程除了默认堆,还有其他的, 比如C/C++ CRT就会创建自己的堆,我们每次new对象,默认都是在该堆里进行的。所以如果我们写程序时每个模块(DLL)用静态方式链接CRT,在DLL内new对象时都会在DLL自己的CRT堆上创建对象。&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
@jk有什么方法, 希望可以学习下。&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
C++的对象,是一个比较大的概念,几乎包含所有东西。。。呵呵&&&&&&
&re: 如何判断一个C++对象是否在堆栈上
呵呵。。。线程也能建立在堆上面的。@Duwen&&&&&&3320人阅读
&&& 1、要求对象分配在堆上:
栈上对象在定义时自动构造,在生存期结束时自动析构。因此可把析构函数声明为私有,这样栈上对象离开作用域时就会出错,不能自动析构。同时为了在堆上能够正确的创建和删除对象,提供一个伪析构函数来访问真正的析构函数。客户端使用时需要调用伪析构函数来销毁堆上的对象。
&&& 例如,对于一个表示无限精度数字的类,要让对象只能创建在堆上,如下:
//upnumber1.hpp:表示无限精度数字的类,其对象只能创建在堆上
#ifndef UPNUMBER_HPP
#define UPNUMBER_HPP
class UPNumber{
UPNumber(){
UPNumber(int val){
UPNumber(double val){
UPNumber(UPNumber const& rhs){
void destroy() const{ //伪析构函数
~UPNumber(); //析构函数私有
//upnumber1test.cpp:对UPNumber,测试对象是否只能创建在堆上
#include &upnumber1.hpp&
int main(){
UPNumber *p=new UPN
p-&destroy(); //调用伪析构函数,不用直接用
UPN //会报错,析构函数为私有,编译器不能创建栈对象(否则不能销毁)
&&& 注意,析构函数声明为私有的类不能被继承,也不能被其他类包含。我们可以放宽一点,把析构函数声明为protected的,这样就可以被继承,包含这个类的其他类可以通过包含这个类对象的指针,而不是对象来达到组合的目的。
&&& 2、判断对象是否在堆上:
堆上的对象肯定通过new调用了operator new,而栈上的对象则没有调用它。因此可以给类增加一个标志变量flag来判断是否调用了operator new,类需要重写operator new来设置这个flag。同时增加一个公有的布尔变量onTheHeap以让客户端判断对象是否在堆上。在所有的构造函数中都需要根据flag是否被设置来初始化onTheHeap的值。
&&& 对上面的UPNumber类,可做如下的设计:
//upnumber2.hpp:表示无限精度数字的类,可判断单个的对象或整个数组内存是否在堆上
#ifndef UPNUMBER_HPP
#define UPNUMBER_HPP
#include &cstddef&
#include &new&
class UPNumber{
void initHeapFlag(){
if(!flag){ //如果没有调用operator new和operator new[]
onTheHeap= //说明是栈对象
onTheHeap= //否则调用了operator new或operator new[],是堆对象
flag= //重置标志位
bool onTheH
static void* operator new(std::size_t size);
static void* operator new[](std::size_t size) throw(std::bad_alloc);
UPNumber(){
initHeapFlag(); //初始化onTheHeap标志
UPNumber(int val){
initHeapFlag();
UPNumber(double val){
initHeapFlag();
UPNumber(UPNumber const& rhs){
initHeapFlag();
bool UPNumber::flag= //静态变量在类外仍然需要定义
void* UPNumber::operator new(std::size_t size){
flag= //设计标志位为true
return ::operator new(size);
void* UPNumber::operator new[](std::size_t size) throw(std::bad_alloc){
return ::operator new[](size);
//upnumber2test.cpp:对UPNumber的测试,判断对象是否在堆上
#include &upnumber2.hpp&
#include &iostream&
int main(){
UPN //在栈上
std::cout&&a.onTheHeap&&std::
UPNumber *b=new UPNumber(); //在堆上
std::cout&&b-&onTheHeap&&std::
UPNumber *num=new UPNumber[10]; //在堆上
std::cout&&num-&onTheHeap&&std:: //相当于输出数组第1个元素是否在堆上
std::cout&&&The UPNumber array's heap flag: &&&std::
for(int i=0;i&10;++i){
//只有第1个对象会输出1表示在堆上,其余都会输出0,但实际上
//它们是在堆上的
std::cout&&num[i].onTheHeap&&std::
&&& 解释:
&&& (1)这样的设计只能判断单个的对象是否在堆上。我们还重写了operator new[],这使得它可以判断整个数组所占的内存是否在堆上,但它却不能判断数组中的每个对象是否在堆上。从测试代码的运行结果就可以看出,对堆上的数组中的每个对象,只有第1个输出为1(即true),其余全部输出为0(即false),但我们知道实际上各个对象都在堆上的。这是因为创建整个数组时,operator new[]只会被调用一次,然后调用10次构造函数来初始化数组中的各个对象。当第1次调用构造函数时,onTheHeap被设为true,然后flag重置为false。后面的构造函数调用由于不会再调用operator new[]了,所以都会把onTheHeap设为false。
&&& (2)事实上,如果要实现在任何情况下都能判断对象是否在堆上,在C++中很难存在一种完全可移植的方案。比如,很多系统上,程序的地址空间都是按线性的顺序排列,栈从地址空间的高端往下增长,堆则从低端往上增长。这就可能通过把对象的地址(通过取址运算符&获得)与栈上的一个变量地址进行比较,若比栈变量地址小,则说明对象肯定在堆上。但并不是所有的系统都这样组织内存,而且很多系统的程序静态存储区会放在堆以下的地址空间中,这样就无法判断静态的栈对象了。这就是一种不可移植的方案,它依赖于系统的底层实现。一般UPNumber类中的实现基本能满足我们要求。因为只要通过第1个对象判断出了整个数组块是分配在堆上的,就自然知道了数组中的每个对象都是在堆上的(虽然它们的判断输出结果有出入)。
&&& (3)按照设计惯例,重写operator new和operator new[],也要重写operator delete和operator delete[],以保持对称。但这里我们只是设置了一下标志,并没有做其他定制性的工作,因此不对称也没关系。另一方面,要注意类的这些成员new和delete函数默认为static的,因为它们在构造对象之前或撤销对象之后运行,没有非静态的成员数据可操纵。
&&& 我们可以把堆判断的功能抽离出来,设计成一个基类。为了使设计更完善,我们直接用一个列表来保存堆上的对象的地址,在重写的operator new中把分配的对象堆内存指针压入列表中。在重写的opeator delete中只要搜索这个列表,看看列表中有没有它的内存指针,有就说明是一个堆对象,需要释放内存,没有就说明是一个栈对象。
//heaptracked.hpp:HeapTracked类可判断单个的对象是否的在堆上
#ifndef HEAP_TRACKED_HPP
#define HEAP_TRACKED_HPP
#include &cstddef&
#include &list&
class HeapTracked{
static std::list&const void*& //存放各个堆对象的指针
class MissingAddress{ }; //异常类
virtual ~HeapTracked()=0; //纯虚函数
static void* operator new(std::size_t size);
static void operator delete(void *ptr);
bool isOnHeap()
std::list&const void*& HeapTracked:: //静态对象在类外仍需定义
HeapTracked::~HeapTracked(){ //纯虚的析构函数必须要有再定义
void* HeapTracked::operator new(std::size_t size){
void *memPtr=::operator new(size); //分配内存
addresses.push_front(memPtr); //把地址压入列表前端
return memP
void HeapTracked::operator delete(void *ptr){
if(ptr==0)
//在列表中查找是否有这个指针
std::list&const void*&::iterator it=
find(addresses.begin(),addresses.end(),ptr);
if(it!=addresses.end()){ //若找到,说明指针指向了堆上的内存
addresses.erase(it); //从列表中移除这个指针
::operator delete(ptr); //释放指针指向的内存
}else{ //否则ptr不是一个堆对象的指针,不能调用operator delete,抛出异常
throw MissingAddress();
bool HeapTracked::isOnHeap() const{
//获取*this对象的真正内存起始地址
const void* rawAddress=dynamic_cast&const void*&(this);
//在列表中查找this指针
std::list&const void*&::iterator it=
find(addresses.begin(),addresses.end(),rawAddress);
return it!=addresses.end();
//heaptrackedtest.cpp:对堆跟踪器HeapTracked类的测试
#include &iostream&
#include &heaptracked.hpp&
class Asset : public HeapTracked{
int main(){
std::cout&&a.isOnHeap()&&std:: //不在堆上
Asset *b=new A
std::cout&&b-&isOnHeap()&&std:: //在堆上
&&& HeapTracked是抽象混合基类,因为它有纯虚函数,因此是抽象基类,不能被实例化,只能被继承,由子类来创建对象。它的很多非虚的成员函数有功能实现,因此又是一般的基类,这些功能代表了各个子类的共性,因此把它称为抽象混合基类。注意当把析构函数声明为纯虚的时,必须同时要有定义,因为子类一定会调用基类的析构函数,这样它就必须有定义,而不只是声明。在使用时,让需要堆判断功能的类直接继承HeapTracked类即可。注意这个类只能判断单个对象是否在堆上,不能判断数组是否在堆上,当然可以通过重写operator new[]来完善。唯一需要解释的就是那个dynamic_cast,它把this指针转换成const void*。因为由多继承或虚基类继承而来的对象会有多个地址,因此我们必须要把一个指针dynamic_cast成void*类型,这就会使它指向对象的真正内存开始处。
&&& 3、禁止对象分配在堆上:
这个比较容易。把operator new/operator delete、operator new[]/operator delete[]都声明为私有即可。注意这样的类不能作为基类,也不能被其他类包含。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:989176次
积分:12059
积分:12059
排名:第796名
原创:162篇
转载:82篇
译文:70篇
评论:152条
(1)(1)(6)(1)(20)(20)(18)(24)(2)(8)(13)(3)(5)(11)(13)(1)(2)(3)(1)(8)(4)(2)(5)(7)(3)(6)(3)(2)(3)(2)(5)(1)(8)(1)(4)(3)(1)(6)(4)(18)(18)(9)(26)(13)C++如何判断两个对象是否是同一个对象?判断两个对象的地址可以吗?_百度知道

我要回帖

更多关于 c 判断对象是否相等 的文章

 

随机推荐