2009-04-13 9 views
2

私は最近this rantに来ました。C++ 'new'演算子に関する問題?

私はかなりの記事で述べたポイントのいくつかを理解していない:

  • 著者はdeletedelete[]の小さな迷惑を言及し、それが実際には必要であると主張しているようだ(コンパイラのために)、ソリューションを提供することはありません。私は何か見落としてますか?機能f()の「専門アロケータ」セクションにおいて
  • 、問題で割り当てを交換して解決することができると思われる:(省略アラインメント)

    // if you're going to the trouble to implement an entire Arena for memory, 
    // making an arena_ptr won't be much work. basically the same as an auto_ptr, 
    // except that it knows which arena to deallocate from when destructed. 
    arena_ptr<char> string(a); string.allocate(80); 
    // or: arena_ptr<char> string; string.allocate(a, 80); 
    arena_ptr<int> intp(a); intp.allocate(); 
    // or: arena_ptr<int> intp; intp.allocate(a); 
    arena_ptr<foo> fp(a); fp.allocate(); 
    // or: arena_ptr<foo>; fp.allocate(a); 
    // use templates in 'arena.allocate(...)' to determine that foo has 
    // a constructor which needs to be called. do something similar 
    // for destructors in '~arena_ptr()'. 
    
  • 過負荷の危険」で::新しい演算子[] '、作者はnew(p) obj[10]をしようとします。代わりに(はるかに少ない曖昧)なぜこの:

    obj *p = (obj *)special_malloc(sizeof(obj[10])); 
    for(int i = 0; i < 10; ++i, ++p) 
        new(p) obj; 
    
  • 'C++でメモリ割り当てをデバッグ'。ここで議論することはできません。

全体記事は重要コンストラクタデストラクタカスタムメモリ管理スキームに位置してクラスを中心に展開しているようです。それは役に立つかもしれませんが、私はそれについて議論することはできませんが、それは共通性にかなり制限されています。

基本的に、新しい配置クラスとクラスごとの割り当て子があります。これらのアプローチでは解決できない問題は何ですか?

また、私はちょうど太くて狂っている場合は、あなたの理想的なC++では、置き換えられるものはoperator newでしょうか?必要に応じて構文を作り直してください。という理想があります。

答えて

5

まあ、理想的なはおそらくいかなる種類の削除も必要ないはずです。ガベージコレクションされた環境を持って、プログラマが全面的な問題を回避できるようにしてください。

暴言で苦情が「私は明示的に知られているタイプのオブジェクトを作成することを余儀なくされて好きではない」

  • 「私はmalloc関数がない道を気に入っ」

    1. に降りてくるように見えます

    彼はあなたがnewnew[]の両方を実装する必要が迷惑な事実について正しいですが、あなたは、Cの意味論の中核を維持するためにStroustrupsの欲求によってそのに強制しています。配列からポインタを伝えることはできないので、コンパイラに自分自身に伝える必要があります。これを修正することはできますが、そうすることは、言語のC部分の意味を根本的に変更することを意味します。あなたは、もはやアイデンティティすべてのCコードの非常に大規模なサブセットを破る

    *(a+i) == a[i] 
    

    を利用することができませんでした。

    だから、あなたは

    • は、アレイのより複雑な概念を実装し、ドープベクトルまたは類似のもので配列を実装し、ポインタ演算の素晴らしさを排除し、言語を持つことができます。

    • はガベージコレクションされているため、独自のdelete規律は必要ありません。

    つまり、Javaをダウンロードできます。あなたはそれが

    • が強く型付けされていないので、言語を変更することで、これを拡張し、そうvoid *アップキャストが解消される型チェック、

    を...しかし、それはあなたがコードを書くことができることを意味しうることコンパイラがそれを見ずにFooをBarに変換します。あなたが望むならば、これはまた、ducktypingを可能にするでしょう。

    これらのことをやったら、PythonやRubyにC-ishの構文があります。

    私はStroustrupがcfront 1.0のテープを送信して以来、C++を書いています。 C++に関わる多くの歴史は、C言語に適合するOO言語を欲しいという希望から出てきました。エッフェルのように、同じ時間に出てきた他の、より満足度の高い言語がたくさんありました。 C++が勝ったようです。私はそれがそれがCの世界に収まるかもしれないので、それがを獲得したと思う。

  • +3

    B-b-b-しかし決定論的ファイナライザは、プログラミングの本質、心臓、魂です! –

    +0

    私は返信するつもりでしたが、私はそれをもっと考えればするほど、その文をそのまま残したいと思うほど、賞賛されます。 –

    +0

    おそらく、他人への警告として。 –

    3

    IMHOは非常に誤解を招いています。私は、著者が細かい詳細を理解しているように見えます。誤解したくなるようです。

    void* operator new(std::size_t size, void* ptr) throw(); 
    

    標準は、上記の関数は以下の特性を有することを定義:

    戻り:PTR私見、引数の欠陥を示す重要な点は、以下の通りです。

    注::意図的に他のアクションを実行しません。

    これを改めて説明する - この機能は意図的に他の動作を行いません。これは非常に重要です。これは、配置の新機能の鍵です。オブジェクトのコンストラクタを呼び出すために使用されています。 のサイズがであることにも注意してください。

    時間のない人には、私の要点を要約すると、「malloc」はC言語では「:: operator new」を使ってC++で行うことができます。唯一の違いは、集約型でない場合、つまり、デストラクタとコンストラクタを呼び出す必要がある型の場合、それらのコンストラクタとデストラクタを呼び出す必要があります。そのような型は明示的にCには存在しないので、 "mallocはそれを良くする"という引数を使用することは有効ではありません。対応する "destroyMe"で呼び出さなければならない特別な "initializeMe"関数を持つ 'C'の構造体を持つ場合、作者によって作られたすべての点は、非集約型のC++構造体と同じようにその構造体に適用されます。

    明示的に彼のいくつかのポイントを取る:

    は多重継承を実装するには、コンパイラは、実際にいくつかのキャストの間、ポインタの値を変更する必要があります。 void *に変換するときに最終的にどの値を必要とするかを知ることはできません。したがって、普通の関数はC++でmallocの役割を果たすことはできません - 適切な戻り値の型はありません。私は上記に言及したよう

    class A1 { }; 
    class A2 { }; 
    class B : public A1, public A2 { }; 
    
    void foo() { 
        void * v = ::operator new (sizeof (B)); 
        B * b = new (v) B(); // Placement new calls the constructor for B. 
        delete v; 
    
        v = ::operator new (sizeof(int)); 
        int * i = reinterpret_cast <int*> (v); 
        delete v' 
    } 
    

    は、我々はB.でのコンストラクタを呼び出すために、新たな配置を必要とする:

    は、これは正しくない、再び::演算子新しいのmallocの役割を果たしています「私」の場合は、void *からint *まで問題なくキャストできますが、新しい配置を再度使用するとタイプチェックが改善されます。

    彼が作るもう一つのポイントは、アライメント要件についてです:新しいcharで返さ

    メモリー[...]必ずしも構造体intlistの整列要件を満たしていません。

    3.7.3.1/2下の標準は言う:

    それはどんな完全なオブジェクト型の ポインタに変換してからアクセスするために使用することができるように返されたポインタは適切に整列されなければなりませんオブジェクトまたは配列が割り当てられた記憶域に格納されます( までは、ストレージは対応する解放機能の呼び出しによって明示的に割り当て解除されます)。

    それは私にはかなり明確に見えます。

    特殊なアロケータの下では、作成者は潜在的な問題を説明します。あなた自身がメモリを割り当てるどの型の引数としてもアロケータを使う必要があり、構築されたオブジェクトは明示的に呼び出されたデストラクタを持つ必要があります。ここでも、アロケータオブジェクトをC構造体の "initalizeMe"コールに渡す方法とはどのように違いますか?

    デストラクタを呼び出すことについては、C++では特別な種類のスマートポインタを簡単に作成できます。スコープ外に出たときにデストラクタを明示的に呼び出すために定義できる「placement_pointer」と呼ばせてください。その結果として、我々は持っている可能性があり:

    template <typename T> 
    class placement_pointer { 
        // ... 
        ~placement_pointer() { 
        if (*count == 0) { 
         m_b->~T(); 
        } 
        } 
        // ... 
        T * m_b; 
    }; 
    
    void 
    f() 
    { 
        arena a; 
    
        // ... 
        foo *fp = new (a) foo;   // must be destroyed 
        // ... 
        fp->~foo(); 
    
        placement_pointer<foo> pfp = new (a) foo; // automatically !!destructed!! 
        // ... 
    } 
    

    私がコメントしたい最後の点は以下の通りです:

    グラム++「配置」オペレータが付属して新しい次のように[]に定義:

    inline void * 
    operator new[](size_t, void *place) 
    { 
        return place; 
    } 
    

    上記のように、このように実装されているだけでなく、標準に準拠する必要があります。

    destjをデストラクタを持つクラスにします。どこかにsizeof(obj [10])バイトのメモリがあり、その場所にobj型の10個のオブジェクトを構築したいとします。 (C++はsizeof(obj [10])を10 * sizeof(obj)と定義しています。この配置演算子new []でこれを行うことはできますか?たとえば、次のコードは、そうするように思われる:

    obj * 
    f() 
    { 
        void *p = special_malloc (sizeof (obj[10])); 
        return new (p) obj[10];  // Serious trouble... 
    } 
    

    残念ながら、このコードは正しくありません。一般に、演算子new []に渡されるsize_t引数が実際に割り当てられる配列のサイズに対応するという保証はありません。

    しかし、彼が定義を提供して強調表示するので、size引数は割り当て関数では使用されません。割り当て関数ははありません。 - 上記の配置式の唯一の影響は、期待通りに10個の配列要素のコンストラクタを呼び出すことです。

    このコードには他にも問題がありますが、著者が挙げたものはありません。