有什么方法可以检测我的类的对象是否在堆栈上创建

Any way to detect whether an object of my class is create on stack?

本文关键字:是否 对象 堆栈 创建 方法 什么 我的 检测      更新时间:2023-10-16

现在我需要检测我的类是否创建为stack/global/thread_local变量,例如:

class Foo {
public:
Foo() {
if(im_on_stack) {
std::cout << "I'm on stack" << std::endl;
} else if(im_in_global) {
std::cout << "I'm in global" << std::endl;
} else if(im_a_thread_local) {
std::cout << "I'm a thread_local" << std::endl;
} else {
std::cout << "I'm on ohters location" << std::endl;
}
}
};
class Bar {
Foo mFoo;
};
Foo gFoo;
thread_local Foo tFoo;
int main() {
Foo lFoo;
}

输出应该是:

I'm on ohters location
I'm in global
I'm a thread_local
I'm on stack

在C++中有什么方法可以做到这一点?

编辑:

我为什么这么做:我正在写一个垃圾收集库,我有一个类,我们称之为gc_ptr,我需要知道这个gc_ptr是不是gc根(在我提到的位置上创建(

第2版

根据gc根的概念,它是一个不在堆上的引用,我可能应该这样问:我能检测我的类是否在堆上创建吗?但我认为堆上还是堆上并没有什么区别。

简短的回答:不,不适用于标准C++。可能有编译器或操作系统特定的解决方案,但没有可移植的。

我想您可以通过在堆栈分配对象的构造函数中检测它们的地址是否接近堆栈分配的变量来进行启发式检测。假设堆栈和堆具有完全不同的内存地址,它应该可以工作。完全未定义的行为,尽管根据标准。例如:

#include <iostream>
#include <memory>
struct A
{
A()
{
int test = 0; // test is on the stack
auto distance = reinterpret_cast<char*>(this) - reinterpret_cast<char*>(&test);
isStack = std::abs(distance) < 1024; // completely arbitrary magic number, will need to experiment
}
bool isStack;
};
int main()
{
std::cout << "stack: " << A().isStack << "n";
std::cout << "stack: " << std::make_unique<A>()->isStack << "n";
}

我不认为可以将这种技术扩展到线程局部变量。您还需要注意复制构造函数和赋值运算符,以处理从堆栈到堆对象的复制,反之亦然。

这不是规范的一部分,所以没有。

如果您告诉我们您正在使用的系统/操作系统,也许我们可以提供帮助-有些系统可能会提供堆栈地址和大小-通常这在嵌入式设备中作为编译器输出(地址(的一部分,以及作为环境/项目配置的一部分的输入(大小(。

我在评论中写了什么:

在一定程度上可以做到:假设,您只需要为特定类型的类使用它:您必须重载new(或在静态创建中封装动态构造(。您必须从具有特定构造函数的基类派生所有考虑的类。将信息从new传递到构造函数有点棘手。在我们的案例中,我们使用了一个全局set,其中new记住了指向已创建实例的指针,相应的构造函数会查看它,以确定创建是否由new完成。这对我们来说已经足够了。(关于其他话题-不知道…(

演示:

#include <iostream>
#include <set>
class Object {
private:
static thread_local std::set<void*> pNewSet;
bool _isNewed;
public:
Object();
Object(const Object&) = delete;
const Object& operator=(const Object&) = delete;
~Object() = default;
static void* operator new(size_t size);
bool isNewed() const { return _isNewed; }
private:
static std::set<void*>& getNewPtrs()
{
static thread_local std::set<void*> pNewSet;
return pNewSet;
}
};
void* Object::operator new(size_t size)
{
std::set<void*> &pNewSet = getNewPtrs();
void *p = ::operator new(size);
if (p) pNewSet.insert(p);
return p;
}
Object::Object(): _isNewed(false)
{
std::set<void*> &pNewSet = getNewPtrs();
std::set<void*>::iterator iter = pNewSet.find((void*)this);
if (iter != pNewSet.end()) {
_isNewed = true;
pNewSet.erase(iter);
}
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";n"; __VA_ARGS__ 
// a global static instance
static Object o;
int main()
{
DEBUG(std::cout << o.isNewed() << 'n');
// a static instance (local scope)
DEBUG(static Object o1);
DEBUG(std::cout << o1.isNewed() << 'n');
// a local instance
DEBUG(Object o2);
DEBUG(std::cout << o2.isNewed() << 'n');
// as members
DEBUG(struct Composed { Object o1, o2; } comp);
DEBUG(std::cout << comp.o1.isNewed() << ' ' << comp.o2.isNewed() << 'n');
// created with new
DEBUG(Object *pO = new Object());
DEBUG(std::cout << pO->isNewed() << 'n');
DEBUG(delete pO);
// created as members in an object created with new
DEBUG(Composed *pComp = new Composed());
DEBUG(std::cout << pComp->o1.isNewed() << ' ' << pComp->o2.isNewed() << 'n');
DEBUG(delete pComp);
}

输出:

std::cout << o.isNewed() << 'n';
0
static Object o1;
std::cout << o1.isNewed() << 'n';
0
Object o2;
std::cout << o2.isNewed() << 'n';
0
struct Composed { Object o1, o2; } comp;
std::cout << comp.o1.isNewed() << ' ' << comp.o2.isNewed() << 'n';
0 0
Object *pO = new Object();
std::cout << pO->isNewed() << 'n';
1
delete pO;
Composed *pComp = new Composed();
std::cout << pComp->o1.isNewed() << ' ' << pComp->o2.isNewed() << 'n';
0 0
delete pComp;

编译器资源管理器上的实时演示

注意:

有意删除Object的复制构造函数和赋值运算符。派生类可以提供复制构造函数,但必须调用Object::Object()
  • 该样本没有考虑new的其他风格(例如operator new[]或自C++17以来的align变体(。这应该在生产代码中完成。

  • 否,但您可以阻止在堆/堆栈上创建对象。

    1. 阻止在堆栈上创建,请使析构函数为私有的/受保护的

      class heap_only
      {
      public:
      void release() const { delete this; }
      protected: 
      ~heap_only() {}
      };
      struct heap_only_deleter
      {
      void operator()( heap_only* p ) { p->release(); }
      };
      using up_heap_only = std::unique_ptr<heap_only, heap_only_deleter>;
      //...
      heap_only ho; // fails
      heap_only* pho = new heap_only();
      pho->release();
      up_heap_only up{ new heap_only() };
      
    2. 防止在堆上创建,请将新运算符设置为私有/受保护

      class stack_only
      {
      protected:
      static void* operator new( std::size_t );
      static void* operator new[]( std::size_t );
      };
      //...
      stack_only* pso = new stack_only(); // fails
      stack_only so;
      

    关于您的编辑,只是为了好玩

    void* pstart;
    bool is_on_stack( void* p )
    {
    int end;
    return pstart >= p && p > &end;
    }
    int globalint;
    int main()
    {
    //
    int start;
    pstart = &start;
    //
    int stackint;
    std::cout << std::boolalpha 
    << is_on_stack( &globalint ) << std::endl
    << is_on_stack( &stackint ) << std::endl
    << is_on_stack( new int );
    //...
    }