有什么方法可以检测我的类的对象是否在堆栈上创建
Any way to detect whether an object of my class is create on stack?
现在我需要检测我的类是否创建为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变体(。这应该在生产代码中完成。
否,但您可以阻止在堆/堆栈上创建对象。
-
要阻止在堆栈上创建,请使析构函数为私有的/受保护的:
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() };
-
要防止在堆上创建,请将新运算符设置为私有/受保护:
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 );
//...
}
- 是否需要删除包含对象的"pair"?
- 是否删除在对象构造过程中创建的对象
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 在这种情况下,java对象是否可以调用本机函数
- 返回指向对象的指针的函数调用是否为 prvalue?
- 具有引用成员的结构是否具有唯一的对象表示形式
- 对象初始化中是否允许指向此成员的指针?
- COM :是否可以查看是否存在对我的某个 COM 对象的进程外引用?我可以释放它吗?
- 堆分配的对象是否存在永不为空的唯一所有者?
- 在函数内创建的对象的范围 - 如果在函数外部存储和访问引用,它们是否有效?
- 是否可以使用一个类来控制 C++ 中另一个类的对象?(阿杜伊诺)
- 在对象指针上调用 Delete 是否会递归删除其动态分配的成员
- QFileSystemModel 对象是否会被删除?
- 具有相同特征的两个对象是否只在内存中存储一次?无论定义它们的函数是什么,都是不同的
- 是否可以使用分配器对象来释放另一个分配器分配的内存?
- 线程调用的函数对对象删除是否安全?
- 将对象的字节复制到数组并再次复制回来是否安全
- std::memmove在同一对象之间是否始终安全
- 类对象在 c++ 中是否具有数据类型?
- 是否可以将不可复制的成员用作使对象不可复制的替代方法?