C ++の「インターフェース」という用語


11

Javaはとを明確に区別classinterfaceます。(C#もそうだと思いますが、経験はありません)。ただし、C ++を記述するとき、クラスとインターフェイスの間に言語による強制的な区別はありません。

そのため、Javaには多重継承がないための回避策としてインターフェイスを常に見てきました。このような区別をすることは、C ++ではarbitrary意的で無意味に感じます。

私は常に「最も明白な方法で物事を書く」アプローチを採用する傾向がありました。そのため、C ++でJavaのインターフェースと呼ばれるものがある場合、例えば:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() = 0;
};

そして、私Fooはおそらくほとんどの実装者が、おそらく私が書くだろういくつかの一般的な機能を共有したいと決めた。

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() {}
protected:
  // If it needs this to do its thing:
  int internalHelperThing(int);
  // Or if it doesn't need the this pointer:
  static int someOtherHelper(int);
};

これにより、これはもはやJavaの意味でのインターフェースではなくなります。

代わりに、C ++には、同じ根本的な継承の問題に関連する2つの重要な概念があります。

  1. virtual 相続
  2. メンバー変数を持たないクラスは、ベースとして使用するときに余分なスペースを占有することはできません

    「基本クラスのサブオブジェクトのサイズがゼロになる場合があります」

    参照

それらのうち、可能な限り#1を避けようとします-それが本当に「最もクリーンな」設計であるシナリオに遭遇することはまれです。ただし、#2は微妙ですが、「インターフェイス」という用語の理解とC ++言語機能の重要な違いです。この結果、私は現在(ほとんど)C ++で「インターフェース」と呼ばれることはなく、基本クラスとそのサイズの観点から話しています。C ++のコンテキストでは、「インターフェイス」は間違った名前だと思います。

多くの人がそのような区別をしないのに、それが私の注意を引いた。

  1. C ++の「インターフェース」内に(たとえばprotected)非virtual関数が存在することを許可することにより、何かを失うことになるのでしょうか (私の気持ちはまったく逆です-共有コードのより自然な場所)
  2. 「インターフェイス」という用語はC ++で意味がありますか?それは純粋なだけを意味するのvirtualですか、それともメンバー変数のないC ++クラスをインターフェイスと呼ぶのが公平でしょうか?

C#には、Java と同じインターフェイスの多重継承、実装の単一継承がありますが、汎用拡張メソッドを使用するinternalHelperThingことにより、ほとんどすべての場合にシミュレートできます。
シェード

仮想メンバーを公開するのは悪い習慣であることに注意してください。
クライム

~Foo() {}抽象クラスのパブリックは、(ほとんど)すべての状況下でエラーです。
ウルフ14

回答:


11

C ++では、「インターフェイス」という用語には、広く受け入れられている定義が1つだけではありません。そのため、使用するときはいつでも正確に言う必要があります。デフォルトの実装、ヘッダーファイル、パブリックメンバーの有無にかかわらず、仮想ベースクラス任意のクラスなど。

あなたの例に関して:Java(およびC#で同様)では、おそらくあなたのコードは懸念の分離を暗示するでしょう:

interface IFoo {/*  ... */} // here is your interface

class FooBase implements IFoo 
{
     // make default implementations for interface methods
}

class Foo extends FooBase
{
}

C ++ではこれ行うことができますが、強制されることはありません。また、クラスにメンバー変数がなく、一部のメソッドのデフォルト実装が含まれている場合、クラスをインターフェースと呼びたい場合は、そうするだけで、話をしているすべての人が意味を知っていることを確認してください。


3
C ++プログラマにとって「インターフェイス」のもう1つの考えられる意味publicは、.hファイルの一部です。
デビッドソーンリー

コード例はどの言語ですか?それは、C ++、私は叫びに約だというの試みがある場合...
クイックス- MONICAは虐待WAS

@Qix:簡単に、投稿をもう一度読んで(コードがJavaであることを明確に述べています)、2000ポイントを超えた場合は、投稿を編集して同等のC ++サンプルコードを追加し、サンプルをさらに作成できます晴れ。
Doc Brown

それがjavaの場合、それはまだ間違っています。いいえ、編集できません。変更するのに十分な文字がありません。Javaは...それはC ++での試みであった場合、私は思っていた理由である、拡張/実装にコロンを使用していません
クイックス- MONICAは虐待WAS

@Qix:より構文的な問題を見つけた場合は、クリスマスプレゼントとして保管できます:
Doc Brown

7

インターフェースが実装(インターフェース-小文字の「i」)と抽象化(インターフェース-大文字の「I」)の両方として概念的に概念であるという意味を混乱させるofに陥ったように思えます)。

サンプルに関しては、最初のコードはクラスにすぎません。あなたのクラスが一方であり、それはそれの行動へのアクセスを可能にする方法を提供するという意味でのインターフェースを、そうではありませんインターフェイスあなたは授業がしたいことを行動の種類を表す抽象化レイヤを提供する、インタフェース宣言の意味で実装します。あなたの投稿に対する Doc Brownの回答は、私がここで話していることを正確に示しています。

インターフェースは、多重継承をサポートしない言語の「回避策」としてしばしば宣伝されていますが、私はそれが難しい真実よりも誤解であると感じています(そして、私はそれを述べることにflameするかもしれません!)。インターフェイスは、クラス間の機能的な互換性またはクラスの実装の抽象化を提供するために、継承される必要も祖先である必要もないという点で、多重継承とはまったく関係ありません。実際、コードをそのように実装したい場合、継承を完全に無効にすることができます-私は完全にそれをお勧めするわけではありませんが、あなたができると言っているだけですやれ。したがって、現実には、継承の問題に関係なく、インターフェイスはクラスタイプを定義する手段を提供し、オブジェクトが相互に通信する方法を決定するルールを確立するため、クラスがサポートする必要のない動作を決定できますその動作を実装するために使用される特定のメソッドを決定します。

C ++の「インターフェース」内に(仮想)保護された非仮想関数が存在することを許可することにより、何かを失うことになりますか?(私の感覚はまったく逆です-共有コードのより自然な場所)

純粋なインターフェースは完全に抽象的であることを意味します。これは、共通の祖先から必ずしも動作を継承していない可能性があるクラス間の互換性の契約を定義できるためです。実装時に、サブクラスで実装動作を拡張できるようにするかどうかを選択します。メソッドがでない場合、virtual子孫クラスを作成する必要があると判断した場合、後でその動作を拡張する機能が失われます。実装が仮想であるかどうかに関係なく、インターフェイスはそれ自体で動作の互換性を定義しますが、クラスはクラスが表すインスタンスに対してその動作の実装を提供します。

「インターフェイス」という用語はC ++で意味がありますか?純粋な仮想のみを意味するのですか、それともメンバー変数のないC ++クラスをインターフェイスと呼ぶのが公平でしょうか?

申し訳ありませんが、C ++で本格的なアプリケーションを実際に作成してから長い時間がかかりました。ここで説明したように、Interfaceは抽象化のためのキーワードであることを思い出します。私はC ++クラスをインターフェイスとは呼ばず、代わりにクラスが上記で説明した意味の範囲内でインターフェイスを持っていると言います。その意味で、用語は意味がありますが、それは本当に文脈に依存します。


1
インターフェースの「持っている」と「存在している」の区別のために+1。
ドックブラウン

6

Javaインターフェースは「回避策」ではなく、ダイアモンド継承のような多重継承の問題のいくつかを回避し、結合を最小限に抑える設計手法を奨励するための意図的な設計決定です。

プロテクトメソッドを使用するインターフェイスは、「継承よりも合成を優先する」必要がある教科書の場合です。セクションのSutterとAlexandrescuの優れたC ++コーディング標準からその名前で引用するには:

継承税の回避:継承は、C ++で2番目に緊密な結合関係であり、友情に次ぐものです。密結合は望ましくないため、可能な場合は避ける必要があります。したがって、継承が実際に設計に利益をもたらすことがわかっていない限り、継承よりも合成を優先します。

インターフェイスにヘルパー関数を含めることで、今では少し入力する手間が省けるかもしれませんが、道を傷つけるカップリングを導入しています。ほとんどの場合、ヘルパー関数を分離してaに渡す方が長期的Foo*です。

STLはこの良い例です。可能な限り多くのヘルパー関数<algorithm>が、コンテナクラスにある代わりに引き出されます。たとえばsort()、パブリックコンテナAPIを使用して作業を行うため、STLコードを変更せずに独自の並べ替えアルゴリズムを実装できることを知っています。この設計により、boostなどのライブラリが有効になり、STLがブーストについて何も知る必要なく、STLを置き換えるのではなく強化します。


2

C ++の「インターフェース」内に(仮想)保護された非仮想関数が存在することを許可することにより、何かを失うことになりますか?(私の感覚はまったく逆です-共有コードのより自然な場所)

あなたはそう思うだろうが、そうでなければ抽象クラスに保護された非仮想メソッドを置くことは、サブクラスを書く人に実装を指示する。そうすることは、その下にあるものを隠すベニアを提供するという純粋な意味でのインターフェースの目的を無効にします。

これは、万能の答えがなく、判断を下すために経験と判断を使用する必要がある場合の1つです。完全に仮想的なクラスのすべての可能なサブクラスFooが常にprotectedメソッドの実装を必要とするという100%の自信をもって言うことができればbar()、それFooは適切な場所です。Baz必要のないサブクラスを作成しbar()たら、Baz必要のないコードにアクセスできるという事実に対応するか、クラス階層を再配置する演習を行う必要があります。前者は良い方法ではなく、後者はそもそも物事を適切に整理するのにかかった数分よりも時間がかかることがあります。

「インターフェイス」という用語はC ++で意味がありますか?純粋な仮想のみを意味するのですか、それともメンバー変数のないC ++クラスをインターフェイスと呼ぶのが公平でしょうか?

C ++標準のセクション10.4では、抽象クラスを使用してインターフェイスを実装することについて言及していますが、正式には定義していません。この用語は、一般的なコンピューターサイエンスの文脈で意味があり、有能な人はだれでも、「Fooなんらかのインターフェース」が何らかの形の抽象化を意味することを理解する必要があります。定義されたインターフェイス構造を持つ言語に触れている人は純粋な仮想だと思うかもしれませんが、実際に作業する必要がある人はだれでもFooその定義を見てから先に進みます。


2
純粋な仮想宣言と、宣言と実装の提供の違いは何ですか?どちらの場合でもbaz、実装がreturn false;必要であり、常に、または同様の実装を使用できます。メソッドがすべてのサブクラスに適用されない場合、どの形式の基本抽象クラスにも属しません。
デビッドソーンリー

1
あなたの最後の文は、私たちが同じページにいると言っていると思います:抽象クラスで保護されたメソッドを持つことができない理由はありませんが、絶対に必要なものよりも継承ツリー内にあるべきではありません。クラスが実装をダミー化する必要がある場合、それはツリー内の適切な場所にないと思います。
Blrfl
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.