1

簡単な質問が 2 つあります。単純に、割り当てまたは中止ルーチンCを頻繁に使用します。xmallocC++で実装しました。これは正しい例外のない実装ですか?

template <typename T>
T *xnew(const size_t n)
{
    T *p = new (std::nothrow) T[n];
    if (p == nullptr)
    {
        cerr << "Not enough memory\n";
        abort();
    }
    return p;
}

int main()
{
    int *p = xnew<int>(5000000000LL);
}

<int>2 番目の質問です。呼び出しからを削除するxnew<int>(5000000000LL);と、コンパイラ (g++ 4.7.2)は、戻り値の型がまだ残っているにもかかわらず、それを推論できなくなります。何故ですか?[T = int]int *

編集:newスローされなくても例外をスローする可能性のあるバージョンを使用する場合、オーバーヘッドはありませんか? 絶対に必要でない場合は、例外を使用したくありません。

4

4 に答える 4

5

なぜこれが必要なのかわかりません。 メモリの割り当てに失敗した場合にnewスローされます。std::bad_alloc例外を処理しないと、 が呼び出されstd::terminateてプログラムが効果的に終了し、 と同じ動作になりxmallocます。

もちろん、コンパイラが例外を実装していない場合、これは変わります。

于 2012-11-22T09:38:57.930 に答える
3

<int>2 番目の質問です。呼び出しから を削除するxnew<int>(5000000000LL);と、コンパイラ (g++ 4.7.2) は [T = int] を推論できなくなりますが、戻り値の型 int * はまだ存在します。何故ですか?

関数テンプレートの引数は、関数呼び出しの引数式の型からのみ推定されます。Tは関数パラメーターにまったく表示されないため、推測できません。

関数呼び出しの戻り値をどうするかは、C++ でのテンプレート引数の推論にはまったく影響しません。int *p = some_function(5000000000LL);thenと書くと、 の戻り値の型であるint* とは限りませんsome_function。これは、コンパイラが の戻り値の型を変換しようとする型ですsome_function

したがって、コンパイラが推測できない近位の理由intは、標準がそれを禁止していることです (少なくとも、診断なしでは)。究極の理由は、C++ の設計者 (おそらく最初は Stroustrup) が、演繹のために考慮されるものを制限し、単純ではないにしても、少なくとも人間の心に理解できるルールを維持したかったからです。

C++ には、部分式の型は部分式自体にのみ依存し、周囲の式には依存しないという規則があります。そして、私の知る限り、例外は1つだけです。それは、関数ポインターまたはメンバー関数ポインターがあいまいな場合です。

void foo();
void foo(int);

void (*pfoo1)() = &foo; // &foo evaluates to a pointer to the void overload
void (*pfoo2)(int) = &foo; // &foo evaluates to a pointer to the int overload
void (*pfoo3)() = (void(*)(int))&foo; // &foo evaluates to the int overload, but doesn't convert to the type of pfoo3 so the line fails.
于 2012-11-22T10:01:20.000 に答える
1

例外のスローを回避したい場合new(何らかの理由で-一部の組み込みプラットフォームのように、例外がサポートされていないプラットフォームで作業している場合)、メモリを割り当てることができないnew_handler場合にプログラムを中止するように指定できます。new

#include <stdlib.h>

#include <iostream>
#include <new>

namespace {
    void new_handler_abort()
    {
        std::cerr << "Not enough memory\n";
        abort();
    }

    struct new_handler_abort_installer {

        new_handler_abort_installer() {
            std::set_new_handler(new_handler_abort);
        }

    };


    // a statically allocated object that does nothing but install the
    //  new_handler_abort() function as the new_handler

    new_handler_abort_installer install_new_handler_abort;
}

プログラムの一部としてこのソースファイルを含めるだけで、プログラムnew_handlerを中止するがインストールされ、newメモリの割り当てに問題が発生します。

でも:

  • この初期化がいつ行われるかは決定論的ではありません(以前main()に呼び出される場合を除きます)。したがって、以前にメモリの問題が発生しmain()た場合は、希望どおりの結果が得られない可能性があります。
  • コンパイラは、例外処理をサポートするコードを追加する場合があります。また、呼び出しごとに発生するコードを含む一部のコンパイラでは、発生しない例外を処理するoperator newために少量のオーバーヘッドが費やされる場合があります(新しいコンパイラはこのオーバーヘッドを回避する場合があります)テーブル駆動型スタックアンワインドを使用することにより、各呼び出しで例外を設定するためにコードを実行する必要がなくなります)。
于 2012-11-22T10:34:55.940 に答える
1

operator<<() をスローする可能性があるため、このコードは 100% 安全であるとは限りません。実際には、スローするにはいくつかのまれな条件が満たされる必要があるため、一般的なケースではありません。

  1. std::cerrマスクにbadbit設定されていexceptions()ます(デフォルトではそうではありません)
  2. 出力中に例外がスローされる

この場合、例外が再スローされ、メモリ リークが発生します。

テンプレート関数呼び出し式からの削除について<int>- もちろん機能しません。コンパイラは、割り当てられる左辺値の型からではなく、呼び出し式自体からのみテンプレート引数の型を推測できました。したがって、自動推定するテンプレート引数は、戻り値の型ではなく、関数の引数になります。

template <class T> T f1();
template <class T> T f2(T);

int a = f1();   // Will not compile, shall be f1<int>();
int b = f2(42); // OK

例外のオーバーヘッドは、実際には実装に依存します。最新のコンパイラは、可能であればそのようなオーバーヘッドを回避するのに十分賢いと思いますが、プラットフォームで確認する必要があります。

于 2012-11-22T10:06:49.617 に答える