3つのルールとは何ですか?


2148
  • オブジェクトのコピーとはどういう意味ですか?
  • 何でコピーコンストラクタコピー代入演算子は
  • いつ自分で申告する必要がありますか?
  • オブジェクトがコピーされないようにするにはどうすればよいですか?

52
投票する前にこのスレッド全体タグwikiを読んでc++-faqください。
sbi 2010年

13
@バイナリ:投票する前に、少なくとも時間をかけてコメントのディスカッションを読んでください。以前はもっと単純なテキストでしたが、フレッドはそれを拡張するように求められました。また、文法的には4つの質問ですが、実際にはいくつかの側面を持つ1つの質問にすぎません。(これに同意しない場合は、それぞれの質問に独自に答えてPOVを証明し、結果に投票してください。)
sbi

1
フレッド、C ++ 1xに関するあなたの答えに興味深い追加があります:stackoverflow.com/questions/4782757/…。これにどのように対処しますか?
sbi

6
関連:
ビッグツー

4
C ++ 11の時点で、これは5のルールなどにアップグレードされていると思います。
paxdiablo 2015

回答:


1795

前書き

C ++は、ユーザー定義型の変数を値セマンティクスで扱います。これは、オブジェクトがさまざまなコンテキストで暗黙的にコピーされることを意味し、「オブジェクトのコピー」が実際に何を意味するかを理解する必要があります。

簡単な例を考えてみましょう:

class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

name(name), age(age)この部分に困惑している場合、これはメンバー初期化子リストと呼ばれます。)

特別なメンバー関数

personオブジェクトをコピーするとはどういう意味ですか?このmain関数は、2つの異なるコピーシナリオを示しています。初期化person b(a);は、コピーコンストラクターによって実行されます。その仕事は、既存のオブジェクトの状態に基づいて新しいオブジェクトを構築することです。割り当てb = aは、コピー割り当て演算子によって実行されます。ターゲットオブジェクトはすでに処理が必要な有効な状態にあるため、そのジョブは一般にもう少し複雑です。

コピーコンストラクターも代入演算子(またはデストラクター)も自分で宣言していないため、これらは暗黙的に定義されています。標準からの引用:

[...]コピーコンストラクターとコピー代入演算子、[...]とデストラクターは特別なメンバー関数です。[ プログラムで明示的に宣言されていない場合、実装はこれらのメンバー関数を一部のクラス型に対して暗黙的に宣言します。 それらが使用される場合、実装はそれらを暗黙的に定義します。[...] エンドノート ] [n3126.pdfセクション12§1]

デフォルトでは、オブジェクトのコピーとは、そのメンバーのコピーを意味します:

非共用クラスXの暗黙的に定義されたコピーコンストラクターは、そのサブオブジェクトのメンバーごとのコピーを実行します。[n3126.pdfセクション12.8§16]

非共用クラスXの暗黙的に定義されたコピー代入演算子は、そのサブオブジェクトのメンバーごとのコピー代入を実行します。[n3126.pdfセクション12.8§30]

暗黙の定義

暗黙的に定義された特別なメンバー関数は次のpersonようになります。

// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

この場合、メンバーごとのコピーがまさに私たちが望むもので nameありage、コピーされるので、自己完結型の独立したpersonオブジェクトを取得します。暗黙的に定義されたデストラクタは常に空です。この場合も、コンストラクターでリソースを取得しなかったため、問題ありません。メンバーのデストラクタは、personデストラクタが終了した後に暗黙的に呼び出されます。

デストラクタの本体を実行し、本体内に割り当てられた自動オブジェクトを破棄した後、クラスXのデストラクタは、Xの直接[...]メンバのデストラクタを呼び出します[n3126.pdf 12.4§6]

リソースの管理

では、これらの特別なメンバー関数をいつ明示的に宣言する必要があるのでしょうか。クラスがリソースを管理するとき、つまり、クラスのオブジェクトがそのリソースを担当するとき。これは通常、リソースがコンストラクターで取得され(またはコンストラクターに渡され)、デストラクタで解放されることを意味します。

以前のC ++に戻ってみましょう。のようなものはなくstd::string、プログラマーはポインターに夢中になりました。personクラスはこのように見えたかもしれません。

class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

今日でも、人々はまだこのスタイルでクラスを作成して問題を抱えています:「人をベクターに押し込んだので、おかしなメモリエラーが発生しました!」デフォルトでは、オブジェクトをコピーすることは、メンバーをコピーすることを意味しますが、nameメンバーをコピーするだけですポインタが指す文字配列ではなく、ポインタをコピーします!これにはいくつかの不快な影響があります。

  1. を介した変更は、を介して監視aできますb
  2. 一度b破壊され、a.nameダングリングポインタです。
  3. aが破棄された場合、ぶら下がりポインタを削除すると、未定義の動作が発生します。
  4. 割り当てでは、割り当てname前に何がポイントされたかが考慮されないため、遅かれ早かれ、場所全体でメモリリークが発生します。

明示的な定義

メンバーごとのコピーには望ましい効果がないため、文字配列の深いコピーを作成するには、コピーコンストラクターとコピー代入演算子を明示的に定義する必要があります。

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

初期化と割り当ての違いに注意してくださいname。メモリリークを防ぐには、に割り当てる前に古い状態を破棄する必要があります。また、フォームの自己割り当てから保護する必要がありx = xます。そのチェックがなければ、delete[] name含む配列削除でしょうソースあなたが書くときので、文字列をx = x、両方this->namethat.name同じポインタを含んでいます。

例外安全

残念ながら、このソリューションはnew char[...]、メモリ不足のために例外をスローすると失敗します。考えられる解決策の1つは、ローカル変数を導入してステートメントを並べ替えることです。

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

これは、明示的なチェックなしで自己割り当ても処理します。この問題のさらに強力な解決策は、コピーアンドスワップのイディオムですが、ここでは例外の安全性の詳細については触れません。私は次のポイントを作成するために例外についてのみ言及しました:リソースを管理するクラスを書くのは難しいです。

コピーできないリソース

ファイルハンドルやミューテックスなど、一部のリソースはコピーできません。その場合は、コピーコンストラクターとコピー代入演算子をprivate定義せずに宣言するだけです。

private:

    person(const person& that);
    person& operator=(const person& that);

または、boost::noncopyableそれらを継承するか、削除済みとして宣言することもできます(C ++ 11以降)。

person(const person& that) = delete;
person& operator=(const person& that) = delete;

3つのルール

場合によっては、リソースを管理するクラスを実装する必要があります。(単一のクラスで複数のリソースを管理しないでください。これは痛みにつながるだけです。)その場合、3つルールを覚えておいてくださいください。

デストラクタ、コピーコンストラクタ、またはコピー代入演算子のいずれかを自分で明示的に宣言する必要がある場合は、おそらく3つすべてを明示的に宣言する必要があります。

(残念ながら、この「規則」は、C ++標準や、私が知っているコンパイラーによって強制されていません。)

5のルール

C ++ 11以降、オブジェクトには2つの特別なメンバー関数(移動コンストラクターと移動割り当て)があります。これらの機能を実装するための5つの州のルール。

署名の例:

class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // Copy Ctor
    person(person &&) noexcept = default;            // Move Ctor
    person& operator=(const person &) = default;     // Copy Assignment
    person& operator=(person &&) noexcept = default; // Move Assignment
    ~person() noexcept = default;                    // Dtor
};

ゼロの法則

3/5のルールは0/3/5のルールとも呼ばれます。ルールのゼロ部分は、クラスの作成時に特別なメンバー関数を一切記述できないことを示しています。

助言

ほとんどの場合、リソースを自分で管理する必要はありませんstd::string。これは、などの既存のクラスがすでに行っているためです。std::stringメンバーを使用した単純なコードと、aを使用した複雑でエラーが発生しやすい代替コードを比較するだけで、char*納得できるはずです。生のポインターメンバーから離れている限り、3つのルールが自分のコードに関係することはほとんどありません。


4
フレッド、私は(A)コピー可能なコードで不適切に実装された割り当てを詳しく説明せず、それが間違っていることを示すメモを付け加え、細かいところの別の場所を見ると、私の投票について気分が良くなります。コードでc&sを使用するか、これらすべてのメンバーの実装をスキップして(B)前半を短くします。これは、RoTとはほとんど関係がありません。(C)移動セマンティクスの導入と、それがRoTにとって何を意味するかについて話し合います。
sbi 2010年

7
しかし、投稿はC / Wにする必要があると思います。用語をほぼ正確に保つこと(つまり、「コピー割り当て演算子」と言うこと、および割り当てがコピーを意味することはできなかった一般的なトラップを利用しないこと)が好きです。
Johannes Schaub-litb 2010年

4
@Prasoon:回答の半分を切り取っても、CW以外の回答の「公正な編集」と見なされるとは思いません。
sbi 2010年

69
C ++ 11(つまり、コンストラクターの移動/割り当て)の投稿を更新すると便利です
Alexander Malakhov

5
@solalito使用後に解放する必要のあるもの:同時実行ロック、ファイルハンドル、データベース接続、ネットワークソケット、ヒープメモリ...
fredoverflow

509

3つルールは C ++の経験則であり、基本的に

クラスで次のいずれかが必要な場合

  • コピーコンストラクタ
  • 代入演算子
  • またはデストラクタ

明示的に定義すると、3つすべてが必要になる可能性があります

これは、通常、3つすべてがリソースの管理に使用され、クラスがリソースを管理する場合、通常はコピーと解放を管理する必要があるためです。

クラスが管理するリソースをコピーするための適切なセマンティクスがない場合は、コピーコンストラクターと代入演算子をとして宣言する(定義しない)ことで、コピーを禁止することを検討してくださいprivate

(C ++標準の新しいバージョン(C ++ 11)がC ++に移動セマンティクスを追加することに注意してください。これにより、3つのルールが変更される可能性があります。しかし、C ++ 11セクションを書くには、これについてあまり詳しくありません。三則について。)


3
コピーを防ぐ別の解決策は、コピーできないクラス(などboost::noncopyable)から(プライベートに)継承することです。また、より明確にすることができます。C ++ 0xと関数を「削除」する可能性はここで役立つと思いますが、構文を忘れていました:/
Matthieu M.

2
@Matthieu:うん、それもうまくいく。しかしnoncopyable、それがstd libの一部でない限り、私はそれをあまり改善とは考えていません。(ああ、削除構文を忘れた場合は、私が知っていたよりも忘れました。:)
sbi

3
@大安:この答えを見てください。ただし、MartinhoRule of Zero守ることをお勧めします。私にとって、これは過去10年間に作られたC ++の最も重要な経験則の1つです。
sbi

3
MartinhoのRule of Zeroがarchive.orgに
Nathan Kidd '27

161

ビッグスリーの法則は上記のとおりです。

それが解決する種類の問題の簡単な例、平易な英語:

デフォルト以外のデストラクタ

あなたはコンストラクタにメモリを割り当てたので、それを削除するためにデストラクタを書く必要があります。そうしないと、メモリリークが発生します。

これは仕事であると思うかもしれません。

問題は、オブジェクトのコピーが作成された場合、コピーは元のオブジェクトと同じメモリをポイントすることです。

これらの1つがデストラクタ内のメモリを削除すると、もう1つが無効なメモリへのポインタ(これはダングリングポインタと呼ばれます)を使用しようとすると、毛むくじゃらになります。

したがって、コピーコンストラクタを作成して、新しいオブジェクトに独自のメモリを割り当てて破棄します。

代入演算子とコピーコンストラクター

クラスのメンバーポインターにコンストラクターでメモリを割り当てました。このクラスのオブジェクトをコピーすると、デフォルトの代入演算子とコピーコンストラクターによって、このメンバーポインターの値が新しいオブジェクトにコピーされます。

つまり、新しいオブジェクトと古いオブジェクトは同じメモリを指しているため、1つのオブジェクトで変更すると、他のオブジェクトでも変更されます。1つのオブジェクトがこのメモリを削除した場合、もう1つのオブジェクトがそれを使用しようとします-eek。

これを解決するには、独自のバージョンのコピーコンストラクターと代入演算子を記述します。バージョンは、新しいオブジェクトに個別のメモリを割り当て、アドレスではなく、最初のポインタが指している値全体にコピーします。


4
したがって、コピーコンストラクタを使用すると、コピーが作成されますが、完全に別のメモリロケーションに作成されます。コピーコンストラクタを使用しない場合は、コピーが作成されますが、同じメモリロケーションを指します。それはあなたが言おうとしていることですか?したがって、コピーコンストラクターのないコピーは、新しいポインターがそこにあるが同じメモリ位置を指していることを意味しますが、ユーザーによって明示的に定義されたコピーコンストラクターがある場合、別のメモリ位置を指すがデータを持つ別個のポインターがあります。
アンブレイカブル2015年

4
すみません、私はこの数年前に返信しましたが、私の返信はまだここにないようです:-(基本的に、はい-わかります:-)
Stefan

1
原則はコピー代入演算子にどのように影響しますか?この答えは、3の法則の3番目が言及される場合に、より役立ちます。
DBedrenko 2017年

1
@DBedrenko、「新しいオブジェクトに独自のメモリを割り当てるようにコピーコンストラクタを記述します...」これは、コピー代入演算子に拡張されるのと同じ原理です。私がそれを明確にしたと思いませんか?
ステファン

2
@DBedrenko、もう少し情報を追加しました。それはそれをより明確にしますか?
ステファン

44

基本的に、デストラクタ(デフォルトのデストラクタではない)がある場合、定義したクラスにメモリが割り当てられていることを意味します。クラスが、一部のクライアントコードまたはユーザーによって外部で使用されているとします。

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

MyClassにプリミティブ型付きメンバーがいくつかしかない場合、デフォルトの代入演算子は機能しますが、MyClassにいくつかのポインターメンバーと、代入演算子を持たないオブジェクトがある場合、結果は予測できません。したがって、クラスのデストラクタで削除するものがある場合は、ディープコピー演算子が必要になる可能性があります。つまり、コピーコンストラクタと代入演算子を提供する必要があります。


36

オブジェクトのコピーとはどういう意味ですか?オブジェクトをコピーする方法はいくつかあります。詳細に言及している可能性が最も高い2種類について、ディープコピーとシャローコピーについて話しましょう。

オブジェクト指向言語を使用している(または少なくともそう想定している)ため、メモリの一部が割り当てられているとしましょう。OO言語なので、割り当てたメモリのチャンクは簡単に参照できます。これらは通常、プリミティブ変数(int、char、byte)または独自の型とプリミティブで構成されるクラスとして定義されているためです。したがって、次のようなCarクラスがあるとします。

class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

ディープコピーとは、オブジェクトを宣言してから、オブジェクトの完全に別のコピーを作成する場合です。2つの完全なメモリセットに2つのオブジェクトが作成されます。

Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

今度は奇妙なことをしましょう。car2が間違ってプログラムされているか、意図的にcar1が作成されている実際のメモリを共有することを意図しているとしましょう。(通常、これを行うのは誤りです。クラスでは、通常、それについては以下で説明します。)car2について尋ねるときはいつでも、car1のメモリ空間へのポインタを実際に解決していると思います...それは、多かれ少なかれ、浅いコピーですです。

//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

そのため、どの言語で書いているかに関係なく、オブジェクトのコピーに関しては、ほとんどの場合、詳細なコピーが必要になるため、意味に注意してください。

コピーコンストラクタとコピー代入演算子とは何ですか?上記ですでに使用しました。Car car2 = car1; 本質的に変数を宣言して1行で割り当てる場合など、コードを入力するとコピーコンストラクターが呼び出されます。つまり、コピーコンストラクターが呼び出されます。等号(-)を使用すると、代入演算子がどうなるかを示しcar2 = car1;ます。通知car2は同じステートメントで宣言されていません。これらの操作のために作成する2つのコードチャンクは、非常によく似ています。実際、典型的なデザインパターンには、最初のコピー/割り当てが正当であることが確認されたら、すべてを設定するために呼び出す別の関数があります-私が書いた長文のコードを見ると、関数はほぼ同じです。

いつ自分で申告する必要がありますか?共有または本番用のコードを記述していない場合は、本当に必要なときにのみ宣言する必要があります。プログラムの言語を「偶然に」使用することを選択し、作成しなかった場合は、プログラム言語が何をするかを認識する必要があります。つまり、コンパイラーのデフォルトを取得します。たとえば、コピーコンストラクタを使用することはほとんどありませんが、代入演算子のオーバーライドは非常に一般的です。足し算、引き算などの意味も上書きできることをご存知ですか?

オブジェクトがコピーされないようにするにはどうすればよいですか?プライベート関数を使用してオブジェクトにメモリを割り当てることができるすべての方法をオーバーライドすることは、妥当な出発点です。本当にコピーしたくない場合は、それを公開して、例外をスローし、オブジェクトをコピーしないことでプログラマーに警告することができます。


5
質問はC ++とタグ付けされました。この疑似コードの説明では、明確に定義された「Rule Of Three」についての説明はほとんどせず、混乱を最小限に抑えています。
sehe 14年

26

いつ自分で申告する必要がありますか?

3つのルールでは、次のいずれかを宣言した場合、

  1. コピーコンストラクタ
  2. コピー代入演算子
  3. デストラクタ

次に、3つすべてを宣言する必要があります。コピー操作の意味を引き継ぐ必要性は、ほとんどの場合、ある種のリソース管理を実行するクラスから生じたという観察から生まれました。

  • 1つのコピー操作で実行されていたリソース管理は、おそらく他のコピー操作でも実行する必要があり、

  • クラスのデストラクタもリソースの管理に参加します(通常はリソースを解放します)。管理される古典的なリソースはメモリでした。これが、メモリを管理するすべての標準ライブラリクラス(たとえば、動的メモリ管理を実行するSTLコンテナ)がすべて「大きな3つ」、つまりコピー操作とデストラクタの両方を宣言する理由です。

3の法則の結果ユーザー宣言のデストラクタの存在が、単純なメンバーごとのコピーがクラスのコピー操作に適切である可能性が低いことを示していることです。つまり、クラスがデストラクタを宣言する場合、コピー操作は正しく行われないため、おそらくコピー操作は自動的に生成されるべきではないことを示唆しています。C ++ 98が採用された時点では、この推論の意味は十分に理解されていなかったため、C ++ 98では、ユーザー宣言のデストラクタの存在は、コピー操作を生成するコンパイラの意欲に影響を与えませんでした。これはC ++ 11でも同じですが、コピー操作が生成される条件を制限すると、レガシーコードが破壊されるためです。

オブジェクトがコピーされないようにするにはどうすればよいですか?

コピーコンストラクターとコピー代入演算子をプライベートアクセス指定子として宣言します。

class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

C ++ 11以降では、コピーコンストラクターと割り当て演算子の削除を宣言することもできます

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

16

既存の回答の多くは、コピーコンストラクター、代入演算子、およびデストラクターに既に触れています。ただし、C ++ 11以降では、移動セマンティックの導入により、これが3を超える可能性があります。

最近、Michael Claisseがこのトピックに触れる講演を行いました:http ://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class


10

C ++の3のルールは、次のメンバー関数の1つに明確な定義がある場合、プログラマーが他の2つのメンバー関数を一緒に定義するという3つの要件の設計と開発の基本原則です。つまり、デストラクタ、コピーコンストラクタ、コピー代入演算子の3つのメンバー関数は必須です。

C ++のコピーコンストラクターは特別なコンストラクターです。これは、既存のオブジェクトのコピーに相当する新しいオブジェクトである新しいオブジェクトを構築するために使用されます。

コピー代入演算子は、同じタイプのオブジェクトの他のオブジェクトに既存のオブジェクトを指定するために通常使用される特別な代入演算子です。

簡単な例があります:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

7
こんにちは、あなたの答えは何も新しいものを追加しません。他の人はより深くより正確に主題をカバーします-あなたの答えはおおよそであり、実際にはいくつかの場所で間違っています(つまり、ここには「必須」はありません。すでに完全に回答されている質問に対するこの種の回答を投稿する間、それは本当にあなたの価値がありません。追加する新しいものがない限り。
マット

1
また、4つの簡単な例があり、3つのルールが話している3 つのうちの2つ何らかの関係があります。混乱が多すぎます。
anatolyg 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.