7

スローされたオブジェクトがメモリ内のどこに格納されているかを知りたいです。だから私はそれのための小さなプログラムを書きました:

#include <iostream>

#define print_dist() int a;\
    do { std::cout << __FUNCTION__ << "() a[" << (long)&a - (long)ptrMainStackBase << "]" << std::endl; } while (0)

#define print_distx(x) \
    do { std::cout << __FUNCTION__ << "() " #x "[" << (long)&x - (long)ptrMainStackBase << "]" << std::endl; } while (0)

#define print_distxy(x, y) \
    do { std::cout << __FUNCTION__ << "() " #x "(ex)[" << (long)&x - (long)y << "]" << std::endl; } while (0)

class CTest
{
    public:
        CTest()
        { std::cout << "CTest::CTest" << std::endl; }

//    private:
        CTest(const CTest&)
        { std::cout << "copy" << std::endl; }
};
const CTest *ptrException;
int *ptrMainStackBase;

void Test2()
{
    print_dist();
    CTest test;
    print_distx(test);
    std::cout << "&test=" << &test << std::endl;
    throw test;
}

void Test1()
{
    print_dist();
    try
    {
        Test2();
    }
    catch (const CTest& test)
    {
        ptrException = &test;
        print_dist();
        print_distx(test);
        print_distxy(test, ptrException);
        std::cout << "&test=" << &test << std::endl;
        throw test;
    }
}

int main()
{
    int b;
    ptrMainStackBase = &b;
    print_dist();
    try
    {
        print_dist();
        Test1();
    }
    catch (const CTest& test)
    {
        print_dist();
        print_distx(test);
        print_distxy(test, ptrException);
        std::cout << "&test=" << &test << std::endl;
    }

    return 0;
}

そしてそれは印刷します:

main() a[-4]
main() a[-8]
Test1() a[-64]
Test2() a[-104]
CTest::CTest
Test2() test[-108]
&test=0x7fffd3b21628 <- test created here on stack
copy
Test1() a[-68]
Test1() test[-140736732956164]
Test1() test(ex)[0]
&test=0xb89090 <- and copied here
copy
main() a[-12]
main() test[-140736732956020]
main() test(ex)[144]
&test=0xb89120 <- and here

オブジェクトをスローすると、最初に通常のスタックから離れた別のスタックにコピーされるようです。これは本当ですか?また、2 つの「例外スタック フレーム」の間に 144 バイトの距離があるのはなぜですか?

4

2 に答える 2

6

オブジェクトをスローすると、実際には最初に一時的な場所にコピーされます。そうしないと、スタックのアンロールによってスタックが範囲外になります。参照によってそれをキャッチすると、次のように未定義の動作が発生します。

void foo() {
   A a;
   throw a;
}

void bar() {
   try {
      foo();
   } catch (A& a) {
      // use a
   }
}

一時的な場所にコピーされていない限りa、 に存在しなくなった変数への参照がありますcatch。これが機能するにAは、パブリック コピー コンストラクターが必要です (VS を使用している場合を除き、プライベート コンストラクターも使用します... )。また、これは参照によってキャッチする正当な理由です。そうしないと、1 つではなく 2 つのコピー構造ができてしまいます。

于 2012-07-01T12:25:32.397 に答える
2

オブジェクトをスローすると、最初に通常のスタックから離れた別のスタックにコピーされるようです。これは本当ですか?

スローされたtestは、例外がキャッチされた時点では存在しません。そのオリジナルtestはすでに破壊されています。したがって、それはコピーでなければならず、新しいオブジェクトは引数やローカル変数とは別に管理されます。つまり、スタック上ではありません。

それはどこに住んでいますか?それは実装次第です。ほとんどの場合、実装が管理するのは動的メモリ (ヒープなど) です。

また、2 つの「例外スタック フレーム」の間に 144 バイトの距離があるのはなぜですか?

catch標準では、例外がブロックで再スローされたときに実装が例外をどのように処理するかについては述べていません。ローカル変数のthrowは必ずコピーを作成する必要があるため、実装する最も簡単な方法throwは常にコピーを作成することです。

于 2012-07-01T12:38:57.160 に答える