Cが「オブジェクト指向」言語と見なされないのはなぜですか?


92

Cには、オブジェクトと見なすことができる「構造」などの独自の準オブジェクトがあるようです(通常は高レベルの方法で考えられます)。

また、Cファイル自体は基本的に別個の「モジュール」ですよね?モジュールも「オブジェクト」のようなものではありませんか?C ++に非常に似ているCが、C ++が高レベルの「オブジェクト指向」である低レベルの「手続き型」言語と見なされる理由について混乱しています

*編集:(説明)なぜ、どこで、「オブジェクト」が何のために、そして何のために線が引かれているのか?


40
すべて-なぜ反対票ですか?基本的な質問ですが、悪いことではありません。
ジョンホプキンス

7
OOの原則をCで効果的に使用できます(そして、良いCコードを書く人は通常そうします)が、最近の多くの言語のように、言語は簡単にするために構築されていません。
-asveikau

5
Cには、データの抽象化に対して、異なる、より単純な、(正直なところ)より明確に定義された(少なくともオープンソースコミュニティの)アプローチがあります。C ++は抽象化の原動力になる傾向があり、多くの素晴らしいことを可能にしますが、それらを適切に使用する[しない]方法を理解しなければならないというコストが伴います。詳細については、私の完全な回答をご覧ください。
ヤムマルコヴィッチ

1
まもなく:構造体はメソッドを持つことができません。(関数へのポインターはそれを完全にカットしません)。
SF。

1
Cでオブジェクトベースのプログラミングを行うことは可能ですが、多少の困難が伴います。しかし、それはそれをオブジェクト指向にしません。
ウェッジ

回答:


101

Cには、オブジェクトと見なすことができる「構造体」などの独自の準オブジェクトがあるようです

あなたと一緒にオブジェクト指向プログラミングのウィキペディアのページを読み伝統的にオブジェクト指向スタイルと考えられているものに対応するCスタイル構造体の機能をチェックしてみましょう。

(OOP)は、「オブジェクト」を使用したプログラミングパラダイムです。データフィールドとメソッドとそれらの相互作用で構成されるデータ構造

C構造体は、フィールドとメソッド、およびそれらの相互作用で構成されていますか?番号。

プログラミング手法には、データの抽象化、カプセル化、メッセージング、モジュール性、多態性、継承などの機能が含まれる場合があります。

C構造体は、これらのことを「ファーストクラス」の方法で実行しますか?いいえ。言語はあらゆる段階であなたに対して機能します。

オブジェクト指向のアプローチは、プログラムの残りの部分から直接アクセスできない場所にデータを配置することをプログラマに奨励します

C構造体はこれを行いますか?番号。

オブジェクト指向プログラムには、通常、さまざまなタイプのオブジェクトが含まれます。各タイプは、管理対象の特定の種類の複雑なデータ、またはおそらく実際のオブジェクトまたは概念に対応します

C構造体はこれを行いますか?はい。

オブジェクトは、データが適切に使用されるようにするために設計された一連の関数内にデータをラップすると考えることができます。

番号。

各オブジェクトは、メッセージを受信し、データを処理し、他のオブジェクトにメッセージを送信できます

構造体自体はメッセージを送受信できますか?いいえ。データを処理できますか?番号。

OOPデータ構造は、「独自の演算子を持ち歩く」傾向がある

これはCで起こりますか?番号。

動的ディスパッチ...カプセル化...サブタイプポリモーフィズム...オブジェクトの継承...オープン再帰...オブジェクトのクラス...クラスのインスタンス...接続されたオブジェクトに作用するメソッド...メッセージの受け渡し.. 。抽象化

C構造体のこれらの機能はありますか?番号。

構造体のどの特性が「オブジェクト指向」だと思いますか?私は見つけることができませんので任意の構造体が定義されているという事実以外のタイプを

これで、もちろん、関数へのポインタであるフィールドを持つ構造体を作成できます。構造体に、仮想メソッドテーブルに対応する関数ポインターの配列へのポインターであるフィールドを持たせることができます。等々。もちろん、CでC ++をエミュレートできます。しかし、それはCでプログラミングする非常に非定型的な方法です。C ++を使用する方が良いでしょう。

また、Cファイル自体は基本的に別個の「モジュール」ですよね?モジュールも「オブジェクト」のようなものではありませんか?

繰り返しますが、オブジェクトのように振る舞うモジュールの特性は何ですか?モジュールは、抽象化、カプセル化、メッセージング、モジュール性、多態性、および継承をサポートしていますか?

抽象化とカプセル化はかなり弱いです。モジュールはモジュール式です。それがモジュールと呼ばれる理由です。メッセージング?メソッド呼び出しがメッセージであり、モジュールがメソッドを含むことができるという意味でのみ。多型?いや。継承?いや。モジュールは「オブジェクト」のかなり弱い候補です。


15
「C ++のエミュレート」(用語)が慣用的なCではないことに反対します。1つの反例は、OSカーネルが通常ファイル操作を実装する方法です。それらはやや「ドメイン固有の」イディオム(たとえば、* nixでのドライバーの作成に制限されている)かもしれませんが、認識可能であり、目的のために十分にきれいに仕事をします。OOの概念を十分に実行するユーザーモードライブラリの例もいくつかあります。Gtk +とそれが依存するライブラリを使用します。それをハックと呼ぶとあなたは正しいかもしれませんが、これらは動作するようにひどいではありません
-asveikau

5
@asveikau:慣習は珍しい(たとえ聞いたことがないとしても)、そして「ハッキング」という考えは、それが定義上、慣用的ではないことを意味しませんか?
アダムロビンソン

19
@asveikau:もちろん、Cプログラムをよりオブジェクト指向にするためにできることはあります。しかし、あなたが言うように、それを行うには規律が必要です。言語機能自体は、カプセル化、ポリモーフィズム、クラス継承などに自然に導かれません。むしろ、開発者はそのパターンを言語に課すことができます。それは、構造体が論理的にオブジェクトと同じものであることを意味しません。ヘック、Lispでもオブジェクト指向スタイルでプログラミングできますが、それはコンスセルがオブジェクトであることを意味しません。
エリックリッパー

3
@asveikau、「自分の」という「イディオム」の解釈が軽度...特異なものであるとお勧めしますか?:)
ベンジョー

8
@BillK:構造体にはCのコードを含めることはできません。構造体には関数ポインターデータ)を含めることができます。関数ポインタはコードではありません。コードは、プログラマーによって作成されたテキストアーティファクトです。
エリックリッパー

46

キーワードは「オブジェクト」ではなく「指向」です。オブジェクトを使用するが構造体のように使用するC ++コードでさえ、オブジェクト指向ではありません。

CとC ++は両方ともOOPを実行できますが(Cのアクセス制御はありません)、Cで実行するための構文は(控えめに言っても)不便ですが、C ++の構文は非常に魅力的です。その点でほぼ同一のコア機能にもかかわらず、Cは手続き型であり、C ++はオブジェクト型です。

オブジェクトのみを使用して実行できる設計を実装するためにオブジェクトを使用するコード(通常は多態性を利用することを意味します)は、オブジェクト指向のコードです。オブジェクト指向言語での継承を使用したとしても、オブジェクトをデータのバッグとほとんど同じように使用するコードは、実際には必要以上に複雑な手続き型コードです。実行時にデータで満たされた構造体で変更される関数ポインタを使用するCのコードは、多態性を実行しているため、手続き指向言語でも「オブジェクト指向」と言えます。


2
+1 Cをオブジェクト指向にすることができます。便利ではありませんが、実行できます。
dietbuddha

VBAでオブジェクトを作成する方が簡単ですが、オブジェクトを「指向」することも考えません。
ジェフ

VBA私は「オブジェクトベース」と呼んでいます。それはオブジェクトを使用しますが、ポリモーフィックではなく、私がやったわずかな作業で、どんな種類のコードアクロバットを試しても、ポリモーフィズムを実行できるかどうかはわかりません。基本的には、カプセル化された手続き型言語です。
キルベン

2
ほとんどの言語は、オブジェクト指向、機能的、またはほぼすべてのスタイルの言語として機能できます。違いは「指向」であり、以前にそのように言われたことは聞いたことがない。私はこれを3回投票して受け入れ、それが正しい答えだと思っています。
ビルK

1
@kylben SQLコードをテーブルに保存してから、抽出して実行する(テーブルをオブジェクトにする)のはそれほど面倒ではありません。これは興味深い実験です。実際には少し魅力的だ...
ビル・K

19

最高レベルのプリンシパルに基づいて:

オブジェクトは、相互リンクされた方法でのデータと動作のカプセル化であり、複数のインスタンスを作成でき、外部インターフェイスがわかっている場合はブラックボックスとして機能する全体として動作します。

構造体にはデータが含まれますが、動作はないため、オブジェクトとは見なされません。

モジュールには動作とデータの両方が含まれていますが、この2つが関連し、確実に複数回インスタンス化できないような方法でカプセル化されていません。

そして、それは継承とポリモーフィズムに入る前です...


モジュールの動作とデータが関連しないのはなぜですか?メンバーは基本的に、モジュール内に存在するデータの「動作」ではありませんか?
ダークテンプラー

4
問題は、それらがカプセル化されておらず、関連していないことです。
エオインキャロル

実際には、オブジェクトはデータではなく動作を中心にしています。データは、OOPの二流の市民です。構造体は、代わりにデータを中心としており、動作はありません。
Sklivvz

1
@Dark Templar-エオインが言ったこと...それは関係の性質にあります。確かにそれは、エミュレートするように、モジュール内の構造体を使用することができますが-どこから来たのか私は見ることができますいくつかの OOの非常に非常に基本的な要素をしかし、あなたがやっているだろうものの多くは、セットにこだわり、プログラマに依存しています言語に強制させるのではなく、自己が課した規則のこと。そして、なぜあなたは気にしますか?あなたはオブジェクト指向の利点を得られません-例えば継承はありません-そしてあなたは自分自身を制限しています。
ジョンホプキンス

これは、正しいとマークされたものよりも良い答えです。
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

6

「構造体」はデータのみです。「オブジェクト指向」の通常の迅速で汚れたテストは、「コードとデータを単一のユニットとしてカプセル化できる構造がありますか?」です。Cはそれを失敗し、手続き型です。C ++はそのテストに合格します。


6
回避策があり、構造体はメンバー関数への静的関数ポインタを持つことができます。明示的なthisポインターが必要になりますが、その場合、データとデータ処理手段は構造内にカプセル化されます。
コーダー

@Brian Knoblauch:しかし、Cファイル自体はデータとコードのカプセル化ではありませんか?
ダークテンプラー

@DarkTemplarある意味、はい、しかし、翻訳単位を作成してコンパイルし、実行時に実行中のプロセスにロードしない限り、これはほとんど役に立ちません。

翻訳単位?>。<
ダークテンプラー

@Dark Templar:翻訳単位とは、ソースファイルに#includedを加えたもので、条件付きで含まれないことで削除されたもの(#if#ifdefなど)を除いたものです。
デビッドソーンリー

5

Cは、C ++と同様に、データ抽象化を提供する機能を備えています。これは、以前に存在したオブジェクト指向プログラミングパラダイムの1つのイディオムです。

  • C構造体はデータを持つことができます(それが主な目的です)
  • C構造体は、関数ポインターをデータとして定義することもできます
  • C構造体には、メソッドと同様に、関連付けられた一連の関数を含めることができます。このポインターのみが暗黙的に渡されませんが、指定した構造体を処理するように設計された各メソッドの最初の引数として明示的に指定する必要があります。C ++は、クラス/構造体メソッドを定義して呼び出すときに、これを自動的に行います。

C ++のOOPは、データを抽象化する手段を拡張します。有害だと言う人もいれば、正しく使用すると良いツールだと考える人もいます。

  • C ++は、タイプを(少なくとも部分的に)識別できる限り、ユーザーが「クラス/構造体のメソッド」に渡​​すことを要求しないことにより、このポインターを暗黙的にします。
  • C ++を使用すると、特定のメソッド(クラス関数)へのアクセスを制限できるため、より「防御的なプログラミング」または「ばか防止」が可能になります。
  • C ++は、導入することでより強力な型安全性を提供することにより、抽象化を促進します。
    1. malloc + castの代わりの新しい演算子
    2. voidポインターの代わりのテンプレート
    3. マクロの代わりに型付き値を受け取るインライン関数
    4. 建てられた多型を自分で実装する必要はありません、それはあなたが作成することができます抽象階層契約および特殊化

しかし、Cがちょうど適切な量の抽象化を完全に実行できる方法と、C ++によって作成されたオーバーヘッドが実際の問題の解決から注意をそらす方法について説教する多くのC「ハッカー」が見つかります。

効率の悪い抽象プログラミングモデルでは、2年後には抽象化があまり効率的ではないことに気づきましたが、今ではすべてのコードが周囲のすべての素晴らしいオブジェクトモデルに依存しており、アプリを書き直さずに修正することはできません。-ライナス・トーバルズ

他の人は、よりバランスの取れた方法でそれを見て、欠点と欠点の両方を受け入れる傾向があります。

Cを使用すると、自分自身を足で簡単に撃つことができます。C ++はそれを難し​​くしますが、それを行うと、足全体が吹き飛ばされます。-Bjarne Stroustrup


2

コインの反対側であるC ++を調べる必要があります。

OOPでは、たとえば、停止、加速、左または右に曲がる車など、抽象的なオブジェクトを考えます(それに応じてプログラムを設計します)。関数のバンドルを持つ構造体は、概念に適合しません。

「実際の」オブジェクトでは、たとえばメンバーを非表示にする必要があります。または、実際の「is a」関係などを継承することもできます。

以下のコメントを読んだ後:( ほとんど)すべてをCで行うことができるのは正しいことです(常に真実です)。

本当に違いを生む唯一のものは、コンパイラによるポリシーの課しです。すなわち、純粋な仮想関数など。しかし、この答えは技術的な問題にのみ関連しますが、主な違いは(言及されているように)、コーディング中にあなたが考える最初の方法だと思います。なぜなら、C ++は、 Cではやや不器用な方法


「関数のバンドル」を含む構造体が概念に適合しない理由は確かではありませんか?ただし、答えは+1
ダークテンプラー

1
アクセス制御(プライベート/保護/パブリック)以外に、構造体を使用して他のすべてを実行できます。
コーダー

1
@DarkTemplar:そうですね、Javaで関数型プログラミングをエミュレートするのと同じように、CでOOPをエミュレートすることができます(人々は実際にそのトピックに関する電子書籍を書いて、実際にそれをします)。しかし、Cはヘルプを提供しないため、C言語はオブジェクト指向ではありません。

1

あなたは自分でそれを言った。Cにはオブジェクトのようなものがありますが、それでもオブジェクトではないため、CはOOP言語とは見なされません。


1
さて、私は疑問があると思います:何がオブジェクトであり、何がそうでないかについて線が引かれているのはなぜですか?
ダークテンプラー

1

オブジェクト指向とは、アーキテクチャパターン(またはメタパターン)と、このパターンを使用して実装または強制するのに役立つ機能を持つ言語の両方を指します。

「OO」設計を実装できます(Gnomeデスクトップは、おそらく純粋なCで行われるOOの最良の例です)。

しかし、オブジェクト指向設計を実装できるということは、言語をオブジェクト指向にするわけではありません。純粋主義者は、「int」や「char」などの基本的な「タイプ」をオーバーライドまたは継承することはできず、Javaは多重継承をサポートしないため、JavaおよびC ++は本当にオブジェクト指向ではないと主張します。しかし、それらは最も広く使用されているオブジェクト指向言語であり、作業コードを生成するために報酬を得るほとんどの「本物の」プログラマーをオブジェクト指向言語と見なしています。

一方、Cは構造のみをサポートします(COBOL、Pascal、および他の多数の手続き言語と同様)、任意のデータに対して任意の関数を使用できるが、ほとんどはこれをバグと見なすという点で、多重継承をサポートすると主張できます機能より。


0

オブジェクト指向の定義を見てみましょう。

  • メッセージング
  • カプセル化
  • 遅延バインディング

Cはこれらの3つを提供しません。特に、最も重要なMessagingを提供していません。


1
私はそれに反対する必要があると思います。Cでのメッセージング(CからのObjective C APIでの作業)を確実に行うことができます。また、関数ポインターを介して遅延バインディングを実行できますが、それを隠すための構文的なシュガーはあまり得られません。(カプセル化はCでは実際に簡単です。不完全な構造体型へのポインターは完全に機能します。)
ドナルフェローズ

継承もオブジェクト指向では重要と見なされ、Cはそれを行いません。Cでのカプセル化に最も近いのは、内部(static)関数とデータメンバーを持つことができる個別のファイルです。
デヴィッド

Objective-Cで渡す実際のメッセージである@DonalFellowsは、C99マクロとして簡単に実装できます。コンパイラが実行する唯一のことは、必要なすべての構造を数行の高レベルコードから静的に生成することです。すべてではないにしても、ほとんどの機能はC APIから利用できます。
QPR

0

OOには多くの重要な要素がありますが、大きなものは、コードの大部分がオブジェクトの内部(実装ではなく表面インターフェースを参照)を知らないこと、オブジェクトの状態が管理対象ユニットであることです(つまり、オブジェクトが停止し、その状態も停止した場合)、一部のコードがオブジェクトの操作を呼び出した場合、その操作が何であるか、または何を含んでいるかを正確に知ることなくそうします(実行するのはパターンに従うだけです壁を越えて「メッセージ」)。

Cはカプセル化をうまく行います。構造体の定義が見えないコードは、その内部を(正規に)覗くことはできません。必要なのは、次のような定義をヘッダーファイルに入れることだけです。

struct FooImpl;
typedef struct FooImpl *Foo;

もちろん、Foos を構築する関数(つまり、ファクトリー)があり、割り当てられたオブジェクト自体に作業の一部を(つまり、「コンストラクター」メソッドを介して)委任する必要があります。 (「デストラクタ」メソッドを介してオブジェクトをクリーンアップしながら)オブジェクトを再度破棄することですが、それは詳細です。

メソッドのディスパッチ(つまり、メッセージング)は、構造体の最初の要素が実際には関数ポインターで満たされた構造体へのポインターであり、それらの各関数ポインターがFoo最初の引数として取得する必要があるという規則によって行うこともできます。ディスパッチは、関数を検索し、適切な引数を書き換えて呼び出すことの問題になります。これは、マクロと少しずるいことで難しくありません。(その関数のテーブルは、C ++のような言語でクラスが実際に何であるかの中核です。)

さらに、これは遅延バインディングも提供します。ディスパッチコードが知っているのは、オブジェクトが指すテーブルへの特定のオフセットを呼び出すことだけです。オブジェクトの割り当てと初期化中にのみ設定する必要があります。実行時のダイナミズムを(速度を犠牲にして)購入するさらに複雑なディスパッチスキームを使用することもできますが、それらは基本的なメカニズムに加えてチェリーです。

しかし、それはCがオブジェクト指向言語であることを意味しません。重要なことは、Cが規約とディスパッチメカニズムを自分で記述する(またはサードパーティのライブラリを使用する)トリッキーな作業をすべてあなたに任せることです。それは大変な仕事です。また、構文またはセマンティックのサポートも提供しないため、完全なクラスシステム(継承など)を実装するのは不必要に苦痛です。OOモデルで適切に記述された複雑な問題を扱っている場合、OO言語はソリューションを書くのに非常に役立ちます。余分な複雑さは正当化できます。


0

Cはオブジェクト指向の概念shrugを実装するのに完璧で適切だと思います。オブジェクト指向と見なされる言語の共通分母サブセット間で見た違いの大部分は、私の種の実際的な観点から見ると、本質的にはマイナーで構文的なものです。

たとえば、情報の隠蔽から始めましょう。Cでは、単純に構造体の定義を非表示にし、不透明なポインターを使用して構造体を操作することで、これを実現できます。それは効果的なモデルpublic対のprivateデータフィールドの区別を私たちはクラスで取得するとして。標準Cライブラリは情報隠蔽を達成するためにこれに大きく依存しているため、それは簡単で反イディオマティックではありません。

もちろん、不透明型を使用してメモリ内の構造を割り当てる正確な場所を簡単に制御する機能は失われますが、これは、たとえばCとC ++の注目すべき違いにすぎません。C ++は、メモリレイアウトの制御を維持しながらCでオブジェクト指向の概念をプログラムする能力を比較する場合、間違いなく優れたツールですが、その点で必ずしもJavaまたはC#がCより優れているという意味ではありません。メモリ内のオブジェクトの割り当て場所を制御する機能を完全に失います。

そして、大きなフープfopen(file, ...); fclose(file);とは対照的な構文を使用する必要がありますfile.open(...); file.close();。誰が本当に気にしますか?IDEのオートコンプリートに大きく依存している人がいるかもしれません。これは実用的な観点からは非常に便利な機能ですが、言語がOOPに適しているかどうかの議論を必要とするものではないかもしれません。

protectedフィールドを効果的に実装する能力はありません。私は完全にそこに提出します。しかし、「すべてのOO言語には、サブクラスが、通常のクライアントからはまだアクセスできない基本クラスのメンバーにアクセスできるようにする機能が必要です」という具体的なルールはないと思います。それに、メンテナンスのハードルになることを少なくとも少し疑っていない保護されたメンバーのユースケースはめったにありません。

そしてもちろん、私たちは類推それらを初期化するために、もう少し定型で動的ディスパッチのためにそれらに関数ポインタとポインタのテーブルをOO多型を「エミュレート」しなければならないvtablesvptrs、しかし、定型の少しは私に多くの悲しみを引き起こしたことはありません。

継承はほぼ同じ方法です。構成によってそれを簡単にモデル化することができ、コンパイラーの内部動作では、同じものに要約されます。私たちがしたい場合はもちろん、私たちは、型の安全性を失うダウンキャスト、そしてあなたがしたい場合はそこに私が言うと思いますダウンキャストのすべての人々がエミュレートするためにCで物事のでそれのためにCを使用しない喜ばせるために、ダウンキャストがタイプから恐ろしいことができます安全性の観点からですが、私はむしろ人々がまったく見落とさないようにしたいです。型安全性は、コンパイラがビットとバイトとして物事を解釈するための非常に多くの余裕を提供するため、Cで簡単に見逃し始めることができますが、コンパイル時に予想されるエラーをキャッチする機能を犠牲にしますが、オブジェクト指向と見なされる言語もあります静的に入力することもできません。

だから知らない、大丈夫だと思う。もちろん、Cを使用して、SOLIDの原則に準拠する大規模なコードベースを作成しようとはしませんが、必ずしもオブジェクト指向の面での短所によるものではありません。このような目的でCを使用しようとした場合に見逃してしまう機能の多くは、強力な型安全性、オブジェクトがスコープ外に出たときに自動的に呼び出されるデストラクタ、演算子など、OOPの前提条件とは直接見なされない言語機能に関連しますオーバーロード、テンプレート/ジェネリック、および例外処理。C ++に到達した補助機能が不足しているときです。


1
コンストラクターをOOPの前提条件とみなすことができなかったのかわかりません。カプセル化の基本的な本質の一部は、オブジェクトの不変条件を保証する機能です。そして、それはそれらの不変条件内でオブジェクトを適切に初期化できることを必要とします。そのためには、コンストラクターが必要です。少なくとも1つの関数が呼び出されるまで、オブジェクトがまだ存在しているとは言えません。
ニコルボーラス

不透明(OPAQUE)型で情報を隠蔽する場合、それらをインスタンス化し、コピー/クローン、および破棄する唯一の方法は、それらの不変式を維持するだけの能力を持つコンストラクターおよびデストラクターとして機能する関数の類似物を使用することです。言語に直接モデル化されていないだけでfoo_createfoo_destroyandおよびの形式に似ている可能性がありますfoo_clone。たとえば

struct Fooこのような場合に不変式を維持する必要があるのは、データ型のメンバーが外部の世界からアクセスおよび変更できないようにすることです。不透明な型(宣言されていますが、外部の世界に定義されていません)がすぐに提供します。これは間違いなく、pimplC ++ の情報隠蔽と同等の、より強力な情報隠蔽形式です。

はい、人々がCでOOPを実装する方法を知っています、ありがとう。私のポイントは、コンストラクターが「直接OOPの前提条件と見なされない」というあなたの声明は誤りであるということでした。
ニコルボーラス

レムはそれを修正しています...しかし、その場合、Cはデストラクタとコンストラクタを(適切に?)サポートしていると言えますか?

-1

悪い質問ではありません。

cをOO言語と呼ぶ場合は、ほぼすべての手続き言語OOも呼び出す必要があります。したがって、この用語は無意味になります。cはOOの言語サポートがありません。構造体がある場合、構造体はtypesクラスではありません。

実際、ほとんどの言語と比較して、cには多くの機能がありません。主に使用されるのは、速度、シンプルさ、人気、および大量のライブラリを含むサポートのためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.