10

C では、動的配列を使用して割り当ててmalloc(sizeof(T) * N)から、ポインター演算を使用して、この動的配列の i オフセットにある要素を取得できます。

C++ ではoperator new()、同じ方法で同じように使用して、malloc()新しい配置を行うことができます (たとえば、Herb Sutter による本「Exceptional C++: 47 engineering puzzles, programming problems, and solutions」の項目 13 の解決策を見ることができます)。持っていない場合、この質問の解決策の要約は次のようになります。

T* storage = operator new(sizeof(T)*size);

// insert element    
T* p = storage + i;
new (p) T(element);

// get element
T* element = storage[i];

サイズ = の N 個の整列された要素を保持するのに十分なメモリを備えたメモリのチャンクを求めているので、私にとってこれは正当に見えましたsizeof(T)sizeof(T)整列された要素のサイズを返す必要があり、それらはメモリのチャンクに次々と配置されるため、ここではポインター演算を使用しても問題ありません。

しかし、私は次のようなリンクを指摘されました: http://eel.is/c++draft/expr.add#4またはhttp://eel.is/c++draft/intro.object#def:objectと主張C++operator new()では配列オブジェクトを返さないため、返されたものに対するポインター演算とそれを配列として使用することは、ANSI C とは対照的に未定義の動作です。

私はそのような低レベルのものが得意ではなく、これを読んで理解しようとしています: https://www.ibm.com/developerworks/library/pa-dalign/またはこれ: http://jrruethe. github.io/blog/2015/08/23/placement-new/しかし、Sutter が単純に間違っていたのか、まだ理解できませんか?

alignas次のような構造では意味があることを理解しています。

alignas(double) char array[sizeof(double)];

(c) http://geordeflanagin.com/alignas.php

配列が境界内にないように見える場合double(おそらくchar、2 バイト読み取りプロセッサで実行された構造体に続く)。

しかし、これは違います - 私はヒープ/空きストレージからメモリを要求しましたsizeof(T)

これが TL;DR の場合に要約すると:

  • malloc()C++ で動的配列を使用することは可能ですか?
  • キーワードoperator new()のない古い C++ で動的配列に newを使用して配置することは可能ですか?alignas
  • によって返されるメモリ上で使用される場合、ポインター演算は未定義の動作operator new()ですか?
  • Sutter は古いマシンで壊れる可能性のあるコードをアドバイスしていますか?

これがばかげている場合は申し訳ありません。

4

4 に答える 4

10

あなたの例のように、割り当てられたメモリでのポインタ演算の問題:

T* storage = static_cast<T*>(operator new(sizeof(T)*size));
// ...
T* p = storage + i;  // precondition: 0 <= i < size
new (p) T(element);

技術的に未定義の動作であることは、長い間知られていました。これstd::vectorは、純粋にライブラリとして明確に定義された動作で実装できないことを意味しますが、標準で見られるものを超えて、実装から追加の保証が必要です。

実装不可能にすることは、標準化委員会の意図ではありませんでしたstd::vector。もちろん、そのようなコードが明確に定義されることを意図しているという Sutter の意見は正しい。規格の文言はそれを反映する必要があります。

P0593は、標準に受け入れられれば、この問題を解決できる可能性がある提案です。その間、上記のようなコードを書き続けても問題ありません。主要なコンパイラはこれを UB として扱いません。

編集:コメントで指摘されているように、 storage + iP0593 で明確に定義されると言ったとき、要素storage[0]storage[1]、 ...storage[i-1]が既に構築されていると想定していたと述べるべきでした。P0593 を十分に理解しているかどうかはわかりませんが、それらの要素がまだ構築されていない場合もカバーしていないと結論付けることができます。

于 2018-11-23T19:28:37.603 に答える
0

"old fashioned"mallocでそれを行うことができます。これにより、それぞれのプラットフォームで最も制限的なアラインメント (たとえば a のアラインメント) を満たすメモリ ブロックが得られますlong long double。したがって、アラインメント要件に違反することなく、そのようなバッファーに任意のオブジェクトを配置できます。

そのため、そのようなメモリ ブロックに基づいて、型の配列に新しい配置を使用できます。

struct MyType {
    MyType() {
        cout << "in constructor of MyType" << endl;
    }
    ~MyType() {
        cout << "in destructor of MyType" << endl;
    }
    int x;
    int y;
};

int main() {

    char* buffer = (char*)malloc(sizeof(MyType)*3);
    MyType *mt = new (buffer)MyType[3];

    for (int i=0; i<3; i++)  {
        mt[i].~MyType();
    }
    free(mt);
}

常に配置 new の場合と同様に、デストラクタを明示的に呼び出し、別のステップでメモリを解放する必要があることに注意してください。これらの 2 つの手順を組み合わせて、所有していないメモリを解放する、deleteまたは -関数を使用しないでください。delete[]

于 2018-11-23T19:32:11.310 に答える