Javaはclass
とinterface
を明確に区別しています。 (C#もそうだと思いますが、C#の経験はありません)。ただし、C++を作成する場合、クラスとインターフェイスの間に言語による区別はありません。
したがって、私は常にインターフェースをJavaの多重継承の欠如に対する回避策と見なしてきました。そのような区別をすることは、C++では恣意的で無意味に感じられます。
私は常に「最も明白な方法で物事を書く」アプローチを採用する傾向があったので、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つの重要な概念があります。
virtual
継承メンバー変数を持たないクラスをベースとして使用すると、余分なスペースを占有できません
「基本クラスのサブオブジェクトはサイズがゼロの場合があります」
それらのうち、私は#1を可能な限り避けようとします-それが本当に「最もクリーンな」設計であるシナリオに遭遇することはまれです。ただし、#2は微妙ですが、「インターフェース」という用語の理解とC++言語機能との重要な違いです。その結果、私は現在(ほとんど)C++で「インターフェース」と呼ぶことはなく、基本クラスとそのサイズの観点から話します。 C++のコンテキストでは、「インターフェース」は誤称です。
多くの人がそのような区別をしているわけではないが、それは私の注意に来ました。
protected
)非virtual
関数の存在を許可することによって、何かを失うことになりますか? (私の気持ちは正反対です-共有コードのより自然な場所)virtual
を意味しますか、それともC++クラスを呼び出さずに公正ですメンバー変数はまだインターフェイスですか?C++では、「インターフェース」という用語は単にone広く受け入れられている定義ではありません。そのため、これを使用する場合は常に、正確に何を意味するかを言う必要があります-デフォルトの実装の有無にかかわらず、仮想基本クラスヘッダーファイル、任意のクラスのパブリックメンバーなど。
あなたの例に関して:Java(そしてC#でも同様))では、あなたのコードはおそらく懸念の分離を意味します:
interface IFoo {/* ... */} // here is your interface
class FooBase implements IFoo
{
// make default implementations for interface methods
}
class Foo extends FooBase
{
}
C++では、canでこれを実行しますが、強制されることはありません。また、クラスにメンバー変数がなく、一部のメソッドのデフォルト実装が含まれている場合に、クラスをインターフェイスと呼びたい場合は、そうするだけですが、話しているすべての人が意味を理解していることを確認してください。
インターフェースは概念的には実装(インターフェース-小文字の「i」)と抽象化(インターフェース-大文字の「I」)の両方として概念的には何であるかという意味を混乱させる罠に陥ったかもしれません。 )。
例に関して言えば、コードの最初のビットは単なるクラスです。あなたのクラスは、その振る舞いへのアクセスを可能にするメソッドを提供するという意味でインターフェースを持っていますが、それはInterfaceは、クラスに実装させたい動作のタイプを表す抽象層を提供するInterface宣言の意味で。 Doc Brownの answer への投稿は、私がここで話していることを正確に示しています。
インターフェースは、多重継承をサポートしない言語の「回避策」としてよく宣伝されていますが、それは難しい真実よりも誤解のほうが多いように感じます(そして、私はそれを述べることで怒られるかもしれません!)。インターフェイスは、クラス間の機能の互換性、またはクラスの実装の抽象化を提供するために継承または祖先である必要がないという点で、多重継承とは何の関係もありません。実際、そのようにコードを実装したい場合は、継承を完全に効果的に廃止することができます-私はそれを完全に推奨するわけではありませんが、私はあなたに言っているだけですできるそれを行う。したがって、実際には、継承の問題に関係なく、インターフェイスはクラス型を定義する手段を提供し、オブジェクトが相互に通信する方法を決定するルールを確立するため、クラスがサポートする必要のない動作を決定できます。その動作を実装するために使用される特定のメソッドを指示します。
C++の「インターフェイス」内に非仮想関数(たとえば、保護された)が存在できるようにすることで、何かを失うことになりますか? (私の気持ちは正反対です-共有コードのより自然な場所)
純粋なインターフェイスは完全に抽象的であることを意味します。これにより、共通の祖先から動作を継承しているとは限らないクラス間の互換性の規約を定義で定義できるからです。実装するときは、実装動作をサブクラスで拡張できるようにするかどうかを選択します。メソッドがvirtual
ではない場合、子孫クラスを作成する必要があると判断した場合に、その動作を拡張する機能を失うことになります。実装が仮想であるかどうかに関係なく、インターフェイスはそれ自体で動作の互換性を定義し、クラスはクラスが表すインスタンスにその動作の実装を提供します。
「インターフェース」という用語はC++で意味がありますか?それは純粋な仮想のみを意味するのでしょうか、それともメンバー変数のないC++クラスをインターフェースに呼び出すことは公正でしょうか?
私を許してください、しかし私が本当にC++で真面目なアプリケーションを書いてから長い時間が経ちました。ここで説明したように、インターフェイスは抽象化のためのキーワードであることを思い出します。私はどのような種類のC++クラスもインターフェイスとは呼びません。代わりに、クラスには上記で概説した意味内のインターフェイスがあると言います。その意味では、ISという用語は意味がありますが、実際には状況によって異なります。
Javaインターフェースは「回避策」ではなく、ダイヤモンドの継承などの多重継承に関する問題のいくつかを回避し、結合を最小限に抑える設計手法を促進するための意図的な設計決定です。
保護されたメソッドとのインターフェースは、「継承よりも構成を優先する」必要がある教科書のケースです。セクションのSutterとAlexandrescuから、その優れた C++ Coding Standards の名前を引用すると、次のようになります。
継承税を回避する:継承はC++で2番目に緊密な結合関係であり、友情に次ぐ。密結合は望ましくないため、可能な限り回避する必要があります。したがって、後者が本当にあなたのデザインに利益をもたらすことを知らない限り、継承よりも構成を優先してください。
ヘルパー関数をインターフェースに含めることで、少しタイプする手間を省くことができるかもしれませんが、将来、あなたを傷つけるカップリングを導入することになります。ヘルパー関数を分離して_Foo*
_で渡すことは、ほとんどの場合より長期的です。
STLはこの良い例です。可能な限り多くのヘルパー関数が、コンテナークラスではなく_<algorithm>
_に引き出されます。たとえば、sort()
はパブリックコンテナーAPIを使用して作業を行うため、STLコードを変更せずに独自の並べ替えアルゴリズムを実装できることがわかります。この設計により、STLがブーストについて何も知る必要なく、STLを置き換える代わりに拡張するブーストのようなライブラリーが可能になります。
C++の「インターフェイス」内に非仮想関数(たとえば、保護された)が存在できるようにすることで、何かを失うことになりますか? (私の気持ちは正反対です-共有コードのより自然な場所)
あなたはそれを考えますが、保護された非仮想メソッドを別の方法で抽象クラスに配置すると、サブクラスを作成するすべての人に実装が必要になります。これを行うと、純粋な意味でのインターフェースの目的が損なわれます。これは、下にあるものを隠すベニアを提供することです。
これは、万能の答えがなく、経験と判断を使用して決定をしなければならない場合の1つです。それ以外の場合は完全に仮想のクラスであるFoo
のすべての可能なサブクラスが常に保護されたメソッドbar()
の実装を必要とするという100%の自信を持って言うことができる場合、Foo
が適切な場所です。 bar()
を必要としないサブクラスBaz
を取得したら、Baz
がコードにアクセスできないようにする必要があるか、クラス階層を再配置する演習を行う必要があります。前者は良い方法ではなく、後者は最初から適切に物事を整えるのにかかる数分よりも時間がかかる場合があります。
「インターフェース」という用語はC++で意味がありますか?それは純粋な仮想のみを意味するのでしょうか、それともメンバー変数のないC++クラスをインターフェースに呼び出すことは公正でしょうか?
C++標準のセクション10.4は、抽象クラスを使用してインターフェースを実装することについて言及していますが、正式には定義していません。この用語は一般的なコンピュータサイエンスのコンテキストで意味があり、「Foo
は(何でも)のインターフェイス」が何らかの形式の抽象化を意味することを理解している必要があります。インターフェース構成が定義された言語に触れる人は、純粋に仮想であると考えるかもしれませんが、実際にFoo
を使用する必要がある人は、先に進む前にその定義を確認します。