質問

いい質問ではのメリットを収集します。私の主な理由を聞こういうBjarne StroustrupとC++によってガベージコレクタです。

ということで、なぜなので追加されてい?既にいくつものゴミ収集のためのC++.それともその一つの"れは"言うは易く行うは難し"タイプはんだろうって。やはりその他の理由なしていると思があることC++11)?

クロスリンク:

かを明確にしないという理解の理由をC++からは、ガベージコレクタが作成されます。私はなぜタンをクリックで追加されます。

役に立ちましたか?

解決

暗黙的なガベージコレクションを追加することもできますが、カットは行われませんでした。おそらく実装の複雑さだけでなく、人々が十分な速さで一般的なコンセンサスに達することができないためです。

Bjarne Stroustrup自身からの引用:

  

ガベージコレクターに   オプションで有効にできます   C ++ 0xの一部になりますが、   私が持っている十分な技術的な問題   ちょうど詳細で間に合わせるために   このようなコレクターの仕様   の残りと統合します   言語(提供されている場合)。そうです   基本的にすべてのC ++ 0x機能を備えた、   実験的な実装が存在します。

こちらのトピックについての良い議論があります。

一般的な概要:

C ++は非常に強力で、ほとんど何でも実行できます。このため、パフォーマンスに影響する可能性のある多くのものが自動的にプッシュされることはありません。ガベージコレクションは、スマートポインター(参照カウントでポインターをラップするオブジェクトで、参照カウントが0に達すると自動的に削除される)を使用して簡単に実装できます。

C ++は、ガベージコレクションのない競合他社を念頭に置いて構築されました。効率性は、C ++がCやその他と比較して批判をかわさなければならない主な関心事でした。

ガベージコレクションには2種類あります...

明示的なガベージコレクション:

C ++ 0xは、shared_ptrで作成されたポインターを介してガベージコレクションを行います

必要な場合は使用できます。不要な場合は使用を強制されません。

C ++ 0xを待ちたくない場合は、現在boost:shared_ptrも使用できます。

暗黙的なガベージコレクション:

ただし、透過的なガベージコレクションはありません。ただし、将来のC ++仕様の焦点になります。

Tr1に暗黙的なガベージコレクションがない理由

C ++ 0xのtr1が持つべき多くのことがあります、以前のインタビューでBjarne Stroustrupはtr1が彼が望んだほど多くを持っていなかったと述べました。

他のヒント

ここで討論に追加するには

ガベージコレクションには既知の問題があり、それらを理解すると、C ++に何も存在しない理由を理解するのに役立ちます。

1。パフォーマンス?

最初の不満は多くの場合パフォーマンスに関するものですが、ほとんどの人は自分が話していることを本当に理解していません。 Martin Beckett で説明されているように、問題はパフォーマンスそのものではなく、パフォーマンスの予測可能性である可能性があります。

現在、広く展開されているGCの2つのファミリがあります:

  • マークアンドスイープの種類
  • 参照カウントの種類

マークアンドスイープは高速ですが(全体的なパフォーマンスへの影響は少ない)、「世界を凍結する」ことに苦しんでいます。 syndrom:つまり、GCが起動すると、GCがクリーンアップするまで他のすべてが停止します。数ミリ秒で応答するサーバーを構築する場合...一部のトランザクションは期待どおりに動作しません:)

参照カウントの問題は異なります。参照カウントは、特にアトミックカウントを必要とするため、マルチスレッド環境でオーバーヘッドを追加します。さらに、参照サイクルの問題があるため、それらのサイクルを検出して除去するための巧妙なアルゴリズムが必要です(通常は「頻度を下げる」ことで実装します)。一般的に、今日の時点で、この種類は(通常、より反応が速いか、むしろフリーズの頻度が低くなりますが) Mark And Sweep よりも遅くなります。

&quotなしで Mark And Sweep と同様のグローバルパフォーマンスを持つ Reference Counting ガベージコレクターを実装しようとしたEiffel実装者による論文を見ました; Freeze The World"アスペクト。 GC用に別のスレッドが必要でした(通常)。アルゴリズムは少し怖いものでした(最後に)が、この論文は概念を一度に1つずつ紹介し、「単純な」ものからのアルゴリズムの進化を示すのに良い仕事をしました。本格的なものへのバージョン。 PDFファイルに手を戻せた場合にのみ読むことをお勧めします...

2。リソースの取得は初期化です

C ++ の一般的なイディオムでは、オブジェクト内のリソースの所有権をラップして、リソースが適切に解放されるようにします。ガベージコレクションがないため、主にメモリに使用されますが、他の多くの状況でも有用です:

  • ロック(マルチスレッド、ファイルハンドルなど)
  • 接続(データベース、別のサーバーなど)

アイデアは、オブジェクトの寿命を適切に制御することです:

  • 必要な限り生き続ける必要があります
  • それが終わったら殺すべきです

GCの問題は、GCが前者を支援し、最終的には後のことを保証する場合...この「究極の」十分ではないかもしれません。ロックを解除する場合は、それ以降の呼び出しがブロックされないように、すぐにロックを解除する必要があります!

GCを使用する言語には2つの回避策があります:

  • スタックの割り当てが十分な場合はGCを使用しないでください。通常はパフォーマンスの問題のためですが、この場合はスコープが有効期間を定義するので本当に役立ちます
  • using コンストラクト...しかし、C ++のRAIIは明示的(弱い)RAIIであるため、ユーザーは無意識のうちに( using キーワードを省略して) )

3。スマートポインター

スマートポインターは、多くの場合、 C ++ のメモリを処理するための特効薬として表示されます。多くの場合、私は聞いたことがあります:スマートポインターがあるため、結局GCは必要ありません。

これ以上は間違っていません。

スマートポインターは役立ちます。 auto_ptr unique_ptr はRAIIの概念を使用しており、非常に便利です。とてもシンプルなので、自分で書くことができます。

どのタイプですか?組み込みの洗濯機コントローラー、携帯電話、ワークステーション、またはスーパーコンピューター向けに最適化する必要がありますか?
GUIの応答性またはサーバーの読み込みを優先すべきですか?
大量のメモリまたは大量のCPUを使用する必要がありますか?

C / c ++は、さまざまな状況で使用されます。 ほとんどのユーザーにとって、スマートポインターのブーストなどで十分だと思います

編集-自動ガベージコレクターはパフォーマンスの問題ではありません(いつでもサーバーを追加購入できます)。これは予測できないパフォーマンスの問題です。
GCがいつ作動するかわからないということは、ほとんどの場合彼らが素晴らしい-本当に応答性が必要な場合に、ナルコレプシー航空のパイロットを雇うようなものです!

C ++にガベージコレクションが組み込まれていない最大の理由の1つは、ガベージコレクションをデストラクタで適切に動作させることが本当に難しいことです。私の知る限り、まだ完全にそれを解決する方法を知っている人はいません。対処すべき問題がたくさんあります:

  • オブジェクトの決定的な寿命(参照カウントはこれを提供しますが、GCはそうではありません。それほど大したことではないかもしれませんが)。
  • オブジェクトがガベージコレクションされているときにデストラクタがスローするとどうなりますか?ほとんどの言語は、この例外を実際に転送できるcatchブロックがないため、この例外を無視しますが、これはおそらくC ++の許容可能な解決策ではありません。
  • 有効/無効にする方法は?当然、コンパイル時の決定でしょうが、GC向けに記述されたコードとNOT GC向けに記述されたコードは大きく異なり、おそらく互換性がありません。これをどのように調整しますか?

これらは直面している問題のほんの一部です。

これは古いの質問ですが、まだ解決されていない問題が1つあります。ガベージコレクションを指定することはほとんど不可能です。

特に、C ++標準は、実装がその動作をどのように達成するかではなく、外部で観察可能な動作の観点から言語を指定することに非常に注意しています。ただし、ガベージコレクションの場合、外部から観察可能な動作はほとんどありません

ガベージコレクションの一般的な考え方は、メモリの割り当てが成功することを保証するための合理的な試みを行うべきであるということです。残念ながら、たとえガベージコレクタが動作していても、メモリの割り当てが成功することを保証することは本質的に不可能です。これはどのような場合でもある程度当てはまりますが、特にC ++の場合はそうです。これは、コレクションサイクル中にメモリ内のオブジェクトを移動するコピーコレクター(または同様のもの)を(おそらく)使用できないためです。

オブジェクトを移動できない場合、割り当てを行うための単一の連続したメモリスペースを作成することはできません。つまり、ヒープ(またはフリーストア、または呼び出したいもの)ができることを意味します。おそらく時間とともに断片化するでしょう。これにより、要求されている量よりも多くのメモリが空いている場合でも、割り当てが成功しないことがあります。

正確に同じ割り当てパターンを繰り返し繰り返し、最初に成功した場合、(本質的に) some 保証を思い付くことができるかもしれませんが、割り当てられたメモリが反復間でアクセス不能になった場合、後続の反復で成功します。それは本質的に役に立たないという非常に弱い保証ですが、それを強化する合理的な希望は見当たりません。

それでも、C ++で提案されているものよりも強力です。 以前の提案 [警告: PDF](ドロップされた)は何も保証しませんでした。 28ページの提案で、外部から観察可能な行動の邪魔になったのは、次のような1つの(非規範的な)メモでした。

  

[注:ガベージコレクションプログラムの場合、高品質のホスト型実装は、回収する到達不能メモリの量を最大化しようとする必要があります。 —メモの終了]

少なくとも私にとって、これは投資収益率についての深刻な質問を提起します。既存のコードを壊し(正確な量は誰にもわからないが、確かにかなり)、実装に新しい要件を課し、コードに新しい制限を課します。

せいぜい、 Javaを使用したテストでは、おそらく現在と同じ速度で実行するには約6倍のメモリが必要になります。さらに悪いことに、ガベージコレクションは最初からJavaの一部でした-C ++はガベージコレクターにさらに十分な制限を課しており、ほぼ確実に偶数の悪いコスト/ベネフィット比を持ちます(たとえ提案が保証されており、何らかの利益があると想定しています)。

状況を数学的に要約します。これは複雑な状況です。数学者なら誰でも知っているように、複素数には実数部と虚数部の2つの部分があります。ここにあるのは現実のコストですが、利益は(少なくともほとんど)想像上のものであるように思えます。

  

自動ガベージコレクションが必要な場合は、優れたコマーシャルがあります   C ++のパブリックドメインガベージコレクター。用途向け   ガベージコレクションは適切で、C ++は優れたガベージコレクションです   他のガベージと好意的に比較するパフォーマンスを持つ言語   収集された言語。 C ++プログラミング言語(4rd   C ++での自動ガベージコレクションの説明については、   Hans-Jも参照してください。 Boehmの CおよびC ++ガベージコレクションのサイトアーカイブ)。

     

また、C ++はメモリを許可するプログラミング手法をサポートしています   管理がガーベッジコレクターなしで安全かつ暗黙的である。ガベージコレクションは最後の選択であり、リソース管理のための不完全な処理方法だと考えています。それは、多くの状況でより良いアプローチがあるというだけで、それが決して役に立たないという意味ではありません。

出典: http://www.stroustrup.com/bs_faq.html#garbage -collection

ビルトインされていない理由については、GCがものになる前に発明されたことを正しく覚えていれば、いくつかの理由で言語にGCがあるとは思わない(IE C)との下位互換性

これがお役に立てば幸いです。

Stroustrupは、2013 Going Nativeカンファレンスでこれについて良いコメントをしました。

このビデオで約25分50秒までスキップしてください。 (実際にビデオ全体を視聴することをお勧めしますが、ガベージコレクションについてはスキップします。)

オブジェクトと値を直接的な方法で処理することを簡単に(そして安全で、予測可能で、読みやすく、教えやすい)本当に素晴らしい言語を持っているとき(明示的)ヒープを使用する場合は、ガベージコレクションもしたくない

最新のC ++およびC ++ 11にあるものでは、ガベージコレクションは限られた状況を除いて望ましくありません。実際、優れたガベージコレクターが主要なC ++コンパイラーの1つに組み込まれていても、それほど頻繁には使用されないと思います。 GCを回避するのは難しくなく、簡単になります。

彼はこの例を示しています:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

これはC ++では安全ではありません。しかし、Javaでも安全ではありません! C ++では、関数が早期に戻る場合、 delete は呼び出されません。ただし、Javaのように完全なガベージコレクションがある場合は、「将来のある時点で」オブジェクトが破棄されるという提案を受け取るだけです。 (更新:これはさらに悪いことです。Javaはファイナライザを呼び出すことを 約束しません-呼び出されない可能性があります)。ガジェットが開いているファイルハンドル、データベースへの接続、または後でデータベースに書き込むためにバッファリングしたデータを保持している場合、これは十分ではありません。これらのリソースをできるだけ早く解放するために、ガジェットが終了したらすぐに破棄する必要があります。不要になった数千のデータベース接続でデータベースサーバーが苦労するのは望ましくありません。プログラムの動作が終了したことはわかりません。

では、解決策は何ですか?いくつかのアプローチがあります。大部分のオブジェクトに使用する明白なアプローチは次のとおりです。

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

これにより、入力する文字が少なくなります。邪魔になる new はありません。 ガジェットを2回入力する必要はありません。オブジェクトは、関数の最後で破棄されます。これがあなたの望むものであれば、これは非常に直感的です。 ガジェットは、 int または double と同じように動作します。予測可能で、読みやすく、教えやすい。すべてが「価値」です。大きな値になることもありますが、ポインター(または参照)で得られるこの「離れた場所でのアクション」を持たないため、値を教えるのは簡単です。

作成するオブジェクトのほとんどは、それらを作成した関数でのみ使用され、おそらく子関数への入力として渡されます。プログラマーは、オブジェクトを返すとき、またはソフトウェアの大きく離れた部分でオブジェクトを共有するときに、「メモリ管理」について考える必要はありません。

範囲と寿命は重要です。ほとんどの場合、有効期間がスコープと同じであれば簡単です。理解しやすく、教えるのも簡単です。異なるライフタイムが必要な場合は、たとえば shared_ptr を使用して、これを実行しているコードを読むのは明らかです。 (または、移動セマンティクスまたは unique_ptr を活用して、値ごとに(大きな)オブジェクトを返します。

これは効率の問題のように見えるかもしれません。 foo()からガジェットを返したい場合はどうすればよいですか? C ++ 11の移動セマンティクスにより、大きなオブジェクトを簡単に返すことができます。 Gadget foo(){...} と記述するだけで、動作し、すぐに動作します。自分で&amp;&amp; をいじる必要はありません。値ごとに返すだけで、言語は必要な最適化を行うことができます。 (C ++ 03より前でも、コンパイラは不必要なコピーを回避するのに非常に良い仕事をしていました。)

Stroustrupがビデオの別の場所で言ったように(言い換え):&quot;

C ++の背後にある考え方は、使用しない機能に対してパフォーマンスへの影響を一切与えないというものでした。したがって、ガベージコレクションを追加すると、Cのようにハードウェア上で直接実行されるプログラムと、ある種のランタイム仮想マシン内で実行されるプログラムが必要になります。

サードパーティのガベージコレクションメカニズムにバインドされたスマートポインターを使用することを妨げるものはありません。 MicrosoftがCOMでそのようなことをしたことを思い出すようですが、うまくいきませんでした。

ほとんどの「なぜ」に回答するにはC ++に関する質問については、 C ++の設計と進化

をご覧ください。

最新のC ++はガベージコレクションを必要としないため。

Bjarne StroustrupのFAQ この問題に関する回答

  

ゴミは好きではありません。ポイ捨てが好きではありません。私の理想は、ガベージを生成しないことでガベージコレクターの必要性を排除することです。これが可能になりました。


最近書かれたコードの状況(C ++ 17以降、公式のコアガイドライン)は次のとおりです。

&quot;ああ?しかし、どうですか...

...昔のC ++を書くために使用した方法でコードを記述する場合はどうですか?&quot;

確かに、あなたはすべてのガイドラインを無視し、漏れやすいアプリケーションコードを書くだけでした。そしていつものようにコンパイルして実行します(漏れます)。

しかし、「それをしないでください」ではありません開発者が好意的であり、多くの自己管理を行使することが期待される状況。準拠していないコードを書くのは簡単ではなく、書くのも速くありませんし、パフォーマンスも良くありません。また、「インピーダンスミスマッチ」の増加に直面するため、徐々に記述が難しくなります。適合コードが提供し、期待するものを使用します。

... reintrepret_cast の場合または、ポインター演算を行いますか?または他のそのようなハッキング?&quot;

確かに、念頭に置いておくと、ガイドラインを順守していても物事を台無しにするコードを書くことができます。しかし:

  1. これを行うことはめったにありません(コード内の場所に関して、必ずしも実行時間の割合に関してではありません)
  2. これを意図的に行うのは偶然ではなく、
  3. そうすることは、ガイドラインに準拠したコードベースで際立っています。
  4. とにかく別の言語でGCをバイパスするようなコードです。

...ライブラリ開発?&quot;

C ++ライブラリ開発者の場合、生のポインターを含む安全でないコードを作成し、慎重にコーディングする必要があります。

元のC言語の背後にある基本原則の1つは、メモリは一連のバイトで構成され、コードはそれらのバイトが使用されている正確な瞬間に何を意味するかだけを考慮する必要があるということです。最新のCでは、コンパイラーは追加の制限を課すことができますが、Cには、ポインターをバイトシーケンスに分解し、同じ値を含むバイトシーケンスをポインターに組み立てて、そのポインターを使用して、以前のオブジェクトにアクセスします。

一部の種類のアプリケーションでは、その機能は有用な場合もあれば、不可欠な場合もありますが、その機能を含む言語は、あらゆる種類の有用で信頼性の高いガベージコレクションをサポートする能力が非常に制限されます。コンパイラーがポインターを構成するビットで行われたすべてのことを知らない場合、ポインターを再構築するのに十分な情報が宇宙のどこかに存在するかどうかを知る方法がありません。その情報を知っていてもコンピュータがアクセスできない方法でその情報を保存することが可能であるため(たとえば、ポインタを構成するバイトは、誰かが書くのに十分な長さで画面に表示されていたかもしれません)それらを紙に書き留めておく)、ポインターが将来使用される可能性があるかどうかをコンピューターが文字通り知ることができない場合があります。

ガベージコレクションされた多くのフレームワークで興味深いのは、オブジェクト参照が、そこに含まれるビットパターンではなく、オブジェクト参照に保持されるビットと他の場所に保持されるその他の情報との関係によって定義されることです。 CおよびC ++では、ポインターに格納されているビットパターンがオブジェクトを識別する場合、そのビットパターンは、オブジェクトが明示的に破棄されるまでそのオブジェクトを識別します。典型的なGCシステムでは、オブジェクトはある時点でビットパターン0x1234ABCDで表されますが、次のGCサイクルは0x1234ABCDへのすべての参照を0x4321BABEへの参照に置き換え、オブジェクトは後者のパターンで表されます。オブジェクト参照に関連付けられたビットパターンを表示し、後でキーボードから読み取ったとしても、同じオブジェクト(または任意のオブジェクト)を識別するために同じビットパターンを使用できるという期待はありません。

すべての技術的な話はコンセプトを複雑にしすぎています。

すべてのメモリのGCを自動的にC ++に入れる場合は、Webブラウザのようなものを検討してください。 Webブラウザは、完全なWebドキュメントをロードし、Webスクリプトを実行する必要があります。ドキュメントスクリプトにWebスクリプト変数を保存できます。多くのタブが開いているブラウザのBIGドキュメントでは、GCが完全なコレクションを実行するたびに、すべてのドキュメント要素もスキャンする必要があることを意味します。

ほとんどのコンピューターでは、これはページフォールトが発生することを意味します。したがって、質問に答える主な理由は、ページフォールトが発生することです。これは、PCが大量のディスクアクセスを開始したときにわかります。これは、無効なポインターを証明するためにGCが大量のメモリにアクセスする必要があるためです。大量のメモリを使用する正真正銘のアプリケーションがある場合、ページフォールトのために、すべてのコレクションをすべてのコレクションをスキャンする必要があります。ページフォールトは、仮想メモリをディスクからRAMに読み戻す必要がある場合です。

したがって、正しいソリューションは、アプリケーションをGCが必要な部分とそうでない部分に分割することです。上記のWebブラウザの例の場合、ドキュメントツリーがmallocで割り当てられていたが、javascriptがGCで実行された場合、GCが起動するたびにメモリの小さな部分とメモリのすべてのPAGED OUT要素のみがスキャンされますドキュメントツリーをページに戻す必要はありません。

この問題をさらに理解するには、仮想メモリとそれがコンピューターにどのように実装されているかを調べてください。 RAMがそれほど多くないときに、プログラムで2GBを使用できるという事実がすべてです。 32BItシステム用に2GB RAMを搭載した最新のコンピューターでは、1つのプログラムのみが実行されている限り、このような問題はありません。

追加の例として、すべてのオブジェクトをトレースする必要がある完全なコレクションを検討してください。まず、ルート経由で到達可能なすべてのオブジェクトをスキャンする必要があります。次に、手順1で表示されているすべてのオブジェクトをスキャンします。次に、待機中のデストラクタをスキャンします。次に、すべてのページに再度移動し、非表示のオブジェクトをすべてオフにします。これは、多くのページが複数回スワップアウトされ、元に戻される可能性があることを意味します。

それで短くするための私の答えは、すべてのメモリに触れた結果として発生するページフォールトの数により、プログラム内のすべてのオブジェクトの完全なGCが実行不可能になるため、プログラマはGCをスクリプトやデータベースのようなものは動作しますが、手動のメモリ管理で通常の動作をします。

そして、もちろん他の非常に重要な理由はグローバル変数です。コレクターがグローバル変数ポインターがGCにあることを知るには、特定のキーワードが必要になるため、既存のC ++コードは機能しません。

短い答え: ガベージコレクションを効率的に(わずかな時間とスペースのオーバーヘッドで)実行する方法がわからず、常に(すべての可能なケースで)正しく実行されます。

長い回答: Cと同様に、C ++はシステム言語です。つまり、オペレーティングシステムなどのシステムコードを記述するときに使用されます。言い換えれば、C ++は、Cと同様に、最高のパフォーマンスを主なターゲットとして設計されています。言語の標準では、パフォーマンスの目標を妨げる可能性のある機能は追加されません。

これにより、「ガベージコレクションがパフォーマンスを妨げる理由」という質問が一時停止します。主な理由は、すべての場合において、実装に関しては、最小限のオーバーヘッドでガベージコレクションを行う方法がわからないということです。したがって、C ++コンパイラとランタイムシステムが常にガベージコレクションを効率的に実行することは不可能です。一方、C ++プログラマーは、彼の設計/実装を知っている必要があり、ガベージコレクションの最適な実行方法を決定するのに最適な人物です。

最後に、制御(ハードウェア、詳細など)とパフォーマンス(時間、スペース、電力など)が主な制約でない場合、C ++は書き込みツールではありません。他の言語は、必要なオーバーヘッドを伴って、より適切に機能し、より多くの[隠された]ランタイム管理を提供します。

を比較するとC++とJavaできなくなります。C++ませんでした設計を暗黙的にごみの収集に心をJavaしました。

このような任意のポインター C-スタイルおよび決定論的destructorsなみの性能GC-実装でも破壊する後方互換性のための大量のC++-レガシー-コードです。

また、C++言語とを目的とする行のドローイングが実行可能になり複雑な実行時の環境です。

すべて:ありが可能である追加のごみ収集C++のためにもの継続性がない方が良いです。コストのそれより大きます。

主に2つの理由:

  1. 必要ないため(IMHO)
  2. C ++の基礎であるRAIIとはほとんど互換性がないため

C ++はすでに手動のメモリ管理、スタック割り当て、RAII、コンテナ、自動ポインタ、スマートポインタを提供しています...それで十分です。ガベージコレクターは、誰がどのオブジェクトを所有すべきか、いつリソースを解放すべきかを5分間考えたくない怠laなプログラマー向けです。それは私たちがC ++で物事を行う方法ではありません。

ガベージコレクションを課すことは、実際には低レベルから高レベルのパラダイムシフトです。

ガベージコレクションのある言語で文字列が処理される方法を見ると、高レベルの文字列操作関数のみが許可され、文字列へのバイナリアクセスは許可されていません。簡単に言えば、すべての文字列関数は最初にポインターをチェックして、文字列がどこにあるかを確認します(1バイトしか描画しない場合でも)。したがって、ガベージコレクションを使用して言語の文字列の各バイトを処理するループを実行している場合、文字列がいつ移動したかを知ることができないため、各反復のベース位置とオフセットを計算する必要があります。次に、ヒープ、スタック、スレッドなどについて考える必要があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top