繁体   English   中英

C ++易失性放置新

[英]C++ Volatile Placement New

如何在易失性指针上执行放置新操作。

例如,我想做这样的事情:

volatile SomeStruct Object;
volatile SomeStruct* thing = &Object;
new (thing) SomeStruct(/*arguments to SomeStruct's constructor*/);

我知道如果没有volatile关键字,这会有用......但是如何使用volatile变量呢?

注意:

Placement new定义如下:

void* operator new(size_t memoryRequested, void* pointer)
{
  return pointer;
}

(顺便说一句,这是GCC如何实现它):

// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }

问题是,我想转换thing类型的volatile SomeStruct*void* ,这是不允许的。

例如,如果我将new运算符更改为:

void* operator new(size_t memoryRequested, volatile void* pointer)
{
  return (void*)pointer;
} 

它会编译,但会调用未定义的行为。

我想说你可以这样做:

new (const_cast<SomeStruct*>(thing)) volatile SomeStruct(...);

但我不确定这是否有效。 问题是,由于分配函数返回一个void*来构造volatile SomeStruct对象,因此对内存的访问可能没有volatile语义,从而导致未定义的行为。

所以我不确定使用placement new将对象构造成一个volatile限定的内存块是否合法。 但是,假设内存最初是一个非易失性的char数组,这似乎是正确的解决方案。

我知道如果没有volatile关键字,这会有用......但是如何使用volatile变量呢?

Placement new与在给定位置构建对象有关。 cv-qualifiers仅在构造对象后应用。 const -ness或volatile -ity仅在构造对象后才适用。 从这个意义上讲,有意义的是,placement new不提供接受volatile (或const )指针的重载。 从C ++标准(草案) [class.ctor / 3]这里 ;

可以为constvolatileconst volatile对象调用构造const volatile constvolatile语义([dcl.type.cv])不适用于正在构造的对象。 它们在最派生对象([intro.object])的构造函数结束时生效。

任何抛弃volatile尝试都会导致未定义的行为, 请参阅此处的cppreference ;

通过非const访问路径修改const对象并通过非易失volatile glvalue引用volatile对象会导致未定义的行为。

另见[expr.const.cast / 6]

鉴于使用volatile和placement new ,问题中的断言(以及一些注释)是该对象需要与信号处理程序一起使用并映射到内存中的特定位置。

虽然有一些替代方案......

如果不需要特定的位置 ,最好不要使用placement new ,只需将volatile qualifer添加到声明它的对象;

struct SomeStruct {
    /*...*/
};
// ...
volatile SomeStruct Object;

如果需要放置newvolatile ,则重新排序它们的使用。 根据需要构造对象,然后添加限定符;

SomeStruct Object;
// ...
void* p = &Object; // or at the required location
volatile SomeStruct* p2 = new (p) SomeStruct;

struct必须是易变的吗? volatile的部分struct可以被内化/抽象和数据的cv修饰符将不需要被暴露给客户端,首先,它是内部处理的struct ;

struct SomeStruct {
    volatile int data;
    void DoSomething()
    {
        data = 42;
    }
};

SomeStruct Object;
/* ... */
void* p = &Object;
auto p2 = new (p) SomeStruct{};
p2->DoSomething();

内部化volatile对象的初始化 ,另一种方法是允许SomeStruct根据需要延迟初始化(或重新初始化/重置)。 鉴于一些明显的限制,这可能不太可行。

struct SomeStruct {
    void Initialise() volatile
    {
        /*...*/
    }
}

我认为这可能会帮助你实现你想要实现的目标。 现在,我向您展示的模板类是使用Windows平台来锁定线程编写的,您可以根据需要修改此类以与其他操作系统 - 平台一起使用。 它仅用于说明如何实现上述语义。 对于Visual Studio 2015 CE,这可以编译,运行和退出,代码为0。 这个类依赖于<Windows.h>头文件来使用CRITICAL_SECTIONEnterCriticalSection()LeaveCriticalSection()InitializeCriticalSection()DeleteCriticalSection() 如果在其他库(如boost库)中有替代方法,则可以轻松编写此类以实现相同的功能。 此类用于在跨多个线程工作时将用户定义的类对象锁定为volatile。

VolatileLocker.h

#ifndef VOLATILE_LOCKER_H
#define VOLATILE_LOCKER_H

#include <Windows.h>

template<typename T>
class VolatileLocker {
private:
    T*  m_pObject;
    CRITICAL_SECTION* m_pCriticalSection;

public:
    VolatileLocker( volatile T& objectToLock, CRITICAL_SECTION& criticalSection );
    ~VolatileLocker();

    T* operator->();

private:
    VolatileLocker( const VolatileLocker& c ); // Not Implemented
    VolatileLocker& operator=( const VolatileLocker& c ); // Not Implemented

}; // VolatileLocker

#include "VolatileLocker.inl"

#endif // VOLATILE_LOCKER_H

VolatileLocker.inl

// ----------------------------------------------------------------------------
// VolatileLocker()
// Locks A Volatile Variable So That It Can Be Used Across Multiple Threads Safely
template<typename T>
VolatileLocker<T>::VolatileLocker( volatile T& objectToLock, CRITICAL_SECTION& criticalSection ) :
    m_pObject( const_cast<T*>( &objectToLock ) ),
    m_pCriticalSection( &criticalSection ) {
    EnterCriticalSection( m_pCriticalSection );
} // VolatileLocker

// ----------------------------------------------------------------------------
// ~VolatileLocker()
template<typename T>
VolatileLocker<T>::~VolatileLocker() {
    LeaveCriticalSection( m_pCriticalSection );
} // ~VolatileLocker

// ----------------------------------------------------------------------------
// operator->()
// Allow The Locked Object To Be Used Like A Pointer
template <typename T>
T* VolatileLocker<T>::operator->() {
    return m_pObject;
} // operator->

VolatileLocker.cpp

#include "VolatileLocker.h"

现在这里是主要运行的应用程序,它使用模板化的volatile locker类和使用placement new运算符。

#include <iostream>
#include "VolatileLocker.h"

static CRITICAL_SECTION s_criticalSection;

class SomeClass {
private:
    int m_value;

public:
    explicit SomeClass( int value ) : m_value( value ) {}

    int getValue() const { return m_value; }

}; // SomeClass

int main() {
    InitializeCriticalSection( &s_criticalSection ); // Initialize Our Static Critical Section

    SomeClass localStackObject( 2 ); // Create A Local Variable On The Stack And Initialize It To Some Value

    // Create A Pointer To That Class And Initialize It To Null.
    SomeClass* pSomeClass = nullptr;
    // Not Using Heap Here, Only Use Local Stack For Demonstration, So Just Get A Reference To The Stack Object
    pSomeClass = &localStackObject;

    // Here Is Our Pointer / Reference To Our Class As A Volatile Object 
    // Which Is Also Locked For Thread Safety Across Multiple Threads
    // And We Can Access The Objects Fields (public variables, methods) via
    // the VolatileLocker's overloaded ->() operator.
    std::cout << VolatileLocker<SomeClass>( *pSomeClass, s_criticalSection )->getValue() << std::endl;

    // Placement New Operator On Our Pointer To Our Object Using The Class's Constructor
    new (pSomeClass) SomeClass( 4 );

    // Again Using The Volatile Locker And Getting The New Value.
    std::cout << VolatileLocker<SomeClass>( *pSomeClass, s_criticalSection )->getValue() << std::endl;

    // Here Is The Interesting Part - Let's Check The Original Local Stack Object
    std::cout << localStackObject.getValue() << std::endl;

    // Cleaning Up Our Critical Section.
    DeleteCriticalSection( &s_criticalSection );
    return 0;
} // main

产量

2
4
4

注意:

需要注意的事情。 初始本地堆栈变量本身不是易失性的。 如果您尝试将堆栈变量声明为volatile并直接使用它:

volatile SomeClass localStackObject( 2 );
SomeClass* pSomeClass = nullptr;
pSomeClass = &localStackObject; // Invalid - volatile SomeClass* cannot be assigned to an entity of type SomeClass*

如果您尝试通过直接使用volatile局部变量解决此问题,您仍然可以将其与VolatileLocker一起使用,但您将无法使用Placement New,因为此代码段显示:

std::cout << VolatileLocker<SomeClass>( localStackObject, s_criticalSection )->getValue() << std::endl; // Line Okay - Notice using object directly and no dereferencing.

// However when we get to this line of code here:
new (localStackObject) SomeClass( 4 ); // Does Not Compile. There Is No Instance Of Operator New To Match The Argument List

// To Fix That We Can Do This:
new ( const_cast<SomeClass*>( &localStackObject) ) SomeClass( 4 ); // This Will Compile

但是,要使用此设计方法访问任何成员,您必须使用VolatileLocker访问类的方法,因此无法直接使用localStackObject。

// This Is Invalid:
std::cout << localStackObject.getValue() << std::endl; 

// Use This Instead:   
std::cout << VolatileLocker<SomeClass>( localStackObject, s_criticalSection )->getValue() << std::endl;

作为一个重要的提醒注意,这个类最初设计时考虑了特定的Windows平台,但是,只需将CRITICAL_SECTION替换为任何可用的跨平台等效函数,就可以轻松编写此模板类的概念,并考虑跨平台模块化。 。

以下是使用基于Linux / Unix的系统的参考答案: stackoverflow / multithreading / linux

以下是使用基于Mac / Apple的系统的参考答案: stackoverflow / multithreading / mac

以下是编写跨平台模块化等价物的参考:

  1. cppreference /线程
  2. cppreference / condition_variable

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM