确定是否将对象分配在静态内存块中(还是如何避免数据竞赛条件)
Identify if object is allocated in static memory block (or how to avoid data race conditions)
序言:
这个问题与这些问题密切相关:...
-C :避免静态初始化订单问题和种族条件同时
- 如何检测位置分配的位置?
...但是它们没有积极的解决方案,我的实际目标用例略有不同。
在构造对象时,我需要知道它是在静态内存BOCK(BSS)中初始化的还是在堆中实例化的。
以下原因:
-
对象本身被设计为在构造函数中初始化为"所有零"的初始化 - 因此,如果对象在静态初始化时不需要初始化 - 当加载程序时,所有对象的整个块已经设置为ZEROS。p>
-
的静态实例该对象可以由其他静态分配的对象使用,并更改对象的某些成员变量
-
静态变量初始化的初始化顺序没有预先确定 - 即可以在调用其构造函数之前调用我的目标对象,从而更改其某些数据,并根据某些未知的构建器来调用构造函数,并根据某些未知的顺序调用。静态的初始化因此清除已经更改的数据。这就是为什么我想在构造函数中禁用静态分配对象的代码。
-
注意:在某些情况下,对象是严重的多线程访问的主题(它具有一定的互锁/减少逻辑),并且必须在任何线程触摸它之前完全初始初始初始化 - 如果我可以保证的是我明确地将其分配在远处,但不在静态区域中(但我也需要静态对象)。
示例代码以说明案例:
struct MyObject
{
long counter;
MyObject() {
if( !isStaticallyAllocated() ) {
counter = 0;
}
}
void startSomething() { InterlockedIncrement(&counter); }
void endSomething() { InterlockedDecrement(&counter); }
};
目前我试图检查是否在某些预定义的范围内"此"指针,但这并不能可靠。
LONG_PTR STATIC_START = 0x00400000;
LONG_PTR STATIC_END = 0x02000000;
bool isStatic = (((LONG_PTR)this >= STATIC_START) && (LONG_PTR)this < STATIC_END));
更新:示例用例不适用新的新操作员。代码是"伪代码",只是为了说明用例。
struct SyncObject() {
long counter;
SyncObject() {
if( !isStaticallyAllocated() ) {
counter = 0;
} }
void enter() { while( counter > 0 ) sleep(); counter++; }
void leave() { counter--; }
}
template <class TEnum>
struct ConstWrapper {
SyncObject syncObj;
TEnum m_value;
operator TEnum() const { return m_value; }
LPCTSTR getName() {
syncObj.enter();
if( !initialized ) {
loadNames();
intialized = true;
}
syncObj.leave();
return names[m_value];
}
}
ConstWrapper<MyEnum> MyEnumValue1(MyEnum::Value1);
您可以通过覆盖类的new
操作员来实现这一目标。在定制的new
中,您可以在分配的内存中设置"魔术字节",您可以在以后检查一下。这将不允许将堆栈与堆区分开,而是静态地与动态分配的对象区分,这可能足够。但是请注意,在以下情况下
class A {
};
class B {
A a;
};
//...
B* b = new B;
b.a将被视为用建议的方法静态分配。
编辑:一个更清洁但更复杂的解决方案可能是新的自定义,您可以在其中跟踪动态分配的内存块。
第二次编辑:如果您只想禁止静态分配,为什么不只是将构造函数私有化并在类中添加出厂功能,然后动态创建对象并提供指针?
class A {
private:
A () { ... }
public:
static A* Create () { return new A; }
};
我认为,控制此目的的最佳方法是为您的班级创建工厂。这样,您可以完全控制如何创建对象,而不是对使用的内存进行复杂的猜测。
第一个答案是:不正常,而且可能根本不可能一些平台。在Solaris的领导下(我也认为Linux),有一个隐式定义的全局符号end
,任意比较地址有效,如果是this < &end
(适当后转换),该变量是静态的,至少只有动态涉及加载。但这远非一般。(绝对无论平台如何,任何时间都涉及动态链接。)
我过去使用的解决方案是手动进行区分。基本上,我设计了课程,以便普通构造函数完成与零初始化相同,然后我提供了一个特殊的no-op用于静态对象的构造函数:
class MayBeStatic
{
public:
enum ForStatic { isStatic };
MayBeStatic() { /* equivalent of zero initialization */ };
MayBeStatic( ForStatic ) { /* do absolutely nothing! */ };
// ...
};
在使用静态寿命定义实例时,您可以使用第二个构造函数:
MayBeStatic object( MayBeStatic::isStatic );
我不认为这是由标准保证的;我觉得允许实现之前修改所需的内存调用构造函数,尤其是我认为它可以允许"重做"零初始化,然后调用构造函数。但是,没有人这样做,所以您可能在实践中安全。
另外,您可以将所有静态实例包装在功能中,以便它们是本地静态,将在第一次初始化功能称为:
MayBeStatic&
getStaticInstance()
{
static MayBeStatic theInstance;
return theInstance;
}
当然,您需要为每个静态实例一个单独的功能。
看起来像是在思考一段时间后,我找到了一个可行的解决方案,可以识别块是否在静态区域。请让我知道,如果有潜在的陷阱。
是为MS Windows设计的,这是我的目标平台 - 由另一个操作系统,我实际上是另一个版本的MS Windows:XP-> Win7。这个想法是获取已加载模块(.exe或.dll)的地址空间,并检查块是否在此地址空间内。计算静态区域启动/结束的代码将放入" lib"段中,因此应在"用户"段的所有其他静态对象之前执行它,即构造函数可以假设staticstart/end变量已经初始化。
#include <psapi.h>
#pragma warning(push)
#pragma warning(disable: 4073)
#pragma init_seg(compiler)
#pragma warning(pop)
HANDLE gDllHandle = (HANDLE)-1;
LONG_PTR staticStart = 0;
LONG_PTR staticEnd = 0;
struct StaticAreaLocator {
StaticAreaLocator() {
if( gDllHandle == (HANDLE)-1 )
gDllHandle = GetModuleHandle(NULL);
MODULEINFO mi;
GetModuleInformation(GetCurrentProcess(), (HMODULE)gDllHandle, &mi, sizeof(mi));
staticStart = (LONG_PTR)mi.lpBaseOfDll;
staticEnd = (LONG_PTR)mi.lpBaseOfDll + mi.SizeOfImage;
// ASSERT will fail in DLL code if gDllHandle not initialized properly
LONG_PTR current_address;
#if _WIN64
ASSERT(FALSE) // to be adopted later
#else
__asm {
call _here
_here: pop eax ; eax now holds the [EIP]
mov [current_address], eax
}
#endif
ASSERT((staticStart <= current_address) && (current_address < staticEnd));
atexit(cleanup);
}
static void cleanup();
};
StaticAreaLocator* staticAreaLocator = new StaticAreaLocator();
void StaticAreaLocator::cleanup() {
delete staticAreaLocator;
staticAreaLocator = NULL;
}
- 如何避免在数据结构中包含存储为字段的类?
- OpenMP:for 循环避免数据竞争,而无需使用关键
- 使用 pybind11 调用 Python 函数时避免复制输入数据
- 如何避免将数据缓冲区的额外副本复制到字符串?
- 在使用 std::cout 和多线程程序中如何避免数据竞争<iomanip>?
- pybind11:如何包装以 std::vector<double> 为参数以避免数据复制的 C++ 函数
- 避免在统计数据和重命名之间进行TOCTOU(检查时间,使用时间)
- 根据数据包类型更改行为,避免开关语句
- 避免在共享库之间传递数据的开销
- 如何避免通过TCP/IP SSH隧道在数据传输中重复读取数据
- 如何避免与"asio::ip::tcp::iostream"的数据竞争?
- 如何避免警告C4244:从'ULONGLONG'转换为"双倍",可能丢失数据
- 指向成员函数的指针与指向数据成员的指针有何不同
- 在创建C 枚举和依赖数据结构时,如何避免重复自己
- 确定是否将对象分配在静态内存块中(还是如何避免数据竞赛条件)
- 文件结构,以避免数据损坏
- 避免在数据未更改时重新计算
- r语言 - 如何直接在数据库服务器上运行C++进程以避免传输数据集
- C++中的内存净化以避免数据泄漏的更好方法
- 如何避免对GUI背后的数据使用全局变量?