C ++に基本クラスがないのはなぜですか?


84

簡単な質問:設計の観点から、C ++には基本クラスの母がないのはobjectなぜですか?通常、他の言語には何がありますか?


27
これは実際には、設計の質問というよりも哲学的な質問です。簡単な答えは、「ビャーネはどうやらそれをしたくなかったからです」です。
ジェリーコフィン

4
また、あなたがそれを作るのを妨げるものは何もありません。
GWW 2011

22
個人的には、すべてを同じ基本クラスから派生させることは設計上の欠陥だと思います。それは、価値があるよりも多くの問題を引き起こすプログラミングのスタイルを促します(実際のオブジェクトを使用するためにケースアップする必要があるオブジェクトの一般的なコンテナを持っている傾向があるため(これは悪いデザインの私見として眉をひそめます))。車とコマンドオートメーションオブジェクトの両方を収容できるコンテナが本当に必要ですか?
マーティンヨーク

3
@Martinですが、次のように見てください。たとえば、C ++ 0x autoまでは、イテレータまたは1回限りの型定義を使用する必要がありましたtypedef。より一般的なクラス階層では、objectまたはを使用できますiterator
slezica 2011

9
@Santiago:統一型システムを使用すると、ほとんどの場合、ポリモーフィズム、動的ディスパッチ、RTTIに依存する多くのコードが作成されます。これらはすべて比較的高価であり、最適化が妨げられます。使用されません。
James McNellis 2011

回答:


116

決定的な判決はStroustrupのFAQにあります。要するに、それは意味的な意味を伝えません。費用がかかります。テンプレートはコンテナに役立ちます。

C ++にユニバーサルクラスのオブジェクトがないのはなぜですか?

  • 必要はありません。ジェネリックプログラミングは、ほとんどの場合、静的に型安全な代替手段を提供します。その他の場合は、多重継承を使用して処理されます。

  • 有用なユニバーサルクラスはありません。真にユニバーサルなクラスには、独自のセマンティクスはありません。

  • 「ユニバーサル」クラスは、タイプとインターフェースについてのずさんな考えを促し、過剰な実行時チェックにつながります。

  • ユニバーサル基本クラスを使用すると、コストが発生します。多態性を持たせるには、オブジェクトをヒープに割り当てる必要があります。これは、メモリとアクセスのコストを意味します。ヒープオブジェクトは、コピーセマンティクスを自然にサポートしていません。ヒープオブジェクトは、単純なスコープ動作(リソース管理を複雑にする)をサポートしていません。ユニバーサル基本クラスは、dynamic_castおよびその他のランタイムチェックの使用を推奨します。


83
必要ありません/基本クラスオブジェクト/必要ありません/思考制御...;)
Piskvorは2011

3
@ Piskvor-LOLミュージックビデオが見たい!
バイロンホイットロック2011

10
Objects must be heap-allocated to be polymorphic-この記述は一般的に正しいとは思いません。スタック上にポリモーフィッククラスのインスタンスを作成し、それをその基本クラスの1つへのポインターとして渡すことで、ポリモーフィックな動作を実現できます。
バイロン

5
Javaでは、reference-to-Fooをreference-to- BarwhenにキャストしようとBarするとFoo、継承されない場合、決定論的に例外がスローされます。C ++では、FooへのポインターをBarへのポインターにキャストしようとすると、ロボットが時間内に戻ってサラ・コナーを殺す可能性があります。
スーパーキャット2015

3
@supercatそれが、dynamic_cast <>を使用する理由です。SkyNetと不思議なことに現れるチェスターフィールドソファを避けるため。
キャプテンキリン

44

まず、そもそもなぜ基本クラスが必要なのかを考えてみましょう。私はいくつかの異なる理由を考えることができます:

  1. あらゆるタイプのオブジェクトで機能する一般的な操作またはコレクションをサポートするため。
  2. すべてのオブジェクトに共通するさまざまな手順(メモリ管理など)を含めるため。
  3. すべてがオブジェクトです(プリミティブはありません!)。一部の言語(Objective-Cなど)にはこれがないため、かなり面倒です。

これらは、Smalltalk、Ruby、Objective-Cブランドの言語に基本クラスがある2つの理由です(技術的には、Objective-Cには実際には基本クラスがありませんが、すべての目的と目的のためにあります)。

#1の場合、C ++にテンプレートを含めることで、単一のインターフェイスですべてのオブジェクトを統合する基本クラスが不要になります。例えば:

void somethingGeneric(Base);

Derived object;
somethingGeneric(object);

パラメトリック多相によって型の整合性をずっと維持できる場合は不要です。

template <class T>
void somethingGeneric(T);

Derived object;
somethingGeneric(object);

#2の場合、Objective-Cでは、メモリ管理プロシージャはクラスの実装の一部であり、基本クラスから継承されますが、C ++のメモリ管理は、継承ではなく構成を使用して実行されます。たとえば、任意のタイプのオブジェクトで参照カウントを実行するスマートポインタラッパーを定義できます。

template <class T>
struct refcounted
{
  refcounted(T* object) : _object(object), _count(0) {}

  T* operator->() { return _object; }
  operator T*() { return _object; }

  void retain() { ++_count; }

  void release()
  {
    if (--_count == 0) { delete _object; }
  }

  private:
    T* _object;
    int _count;
};

次に、オブジェクト自体でメソッドを呼び出す代わりに、ラッパーでメソッドを呼び出すことになります。これにより、より一般的なプログラミングが可能になるだけでなく、関心の分離も可能になります(理想的には、オブジェクトは、さまざまな状況でメモリを管理する方法よりも、何をすべきかについてより関心を持つ必要があります)。

最後に、プリミティブとC ++のような実際のオブジェクトの両方を備えた言語では、基本クラス(すべてのユーザーに一貫したインターフェイス)を使用することの利点値)が失われます。これは、そのインターフェイスに準拠できない特定の値があるためです。このような状況でプリミティブを使用するには、プリミティブをオブジェクトに持ち上げる必要があります(コンパイラが自動的に実行しない場合)。これは多くの複雑さを生み出します。

したがって、あなたの質問に対する簡単な答え:C ++には基本クラスがありません。これは、テンプレートを介したパラメトリック多態性があるため、必要がないためです。


大丈夫です!お役に立ててうれしいです:)
Jonathan Sterling

2
ポイント3については、C#で対抗します。C#には、()にボックス化できるプリミティブがありますが、そうである必要はありません。コンパイラ、およびエイリアスであり、互換的に使用することができます。ランタイムは、必要に応じてボクシングを処理します。objectSystem.ObjectintSystem.Int32
コールジョンソン

C ++では、ボクシングの必要はありません。テンプレートを使用すると、コンパイラはコンパイル時に正しいコードを生成します。
ロブK

2
この(古い)回答は参照カウントされたスマートポインターを示していますが、C ++ 11にはstd::shared_ptr代わりに使用する必要があることに注意してください。

15

C ++変数の主要なパラダイムは、値渡しであり、参照渡しではありません。すべてをルートから派生させるように強制するとObject、値で渡すと事実上エラーになります。

(パラメーターとして値によってオブジェクトを受け入れるため、定義上、オブジェクトをスライスしてその魂を取り除きます)。

これは歓迎されません。C ++を使用すると、値または参照セマンティクスのどちらが必要かを考えて、選択することができます。これは、パフォーマンスコンピューティングにおいて重要なことです。


1
それ自体はエラーではありません。タイプ階層はすでに存在し、適切な場合は値渡しを非常にうまく利用します。ただし、参照渡しがより適切なヒープベースの戦略を奨励します。
Dennis Zickefoose 2011

IMHO、優れた言語は、派生クラスが参照によるスライスを使用して基本クラスオブジェクトとして合法的に使用できる状況、値ごとのスライスを使用して1つに変換できる状況に対して、異なる種類の継承を持つ必要があります。どちらも合法ではないもの。スライスできるものに共通のベースタイプを持つことの価値は限られていますが、多くのものはスライスできず、間接的に保存する必要があります。そのようなものをコモンから派生させるための追加コストはObject比較的わずかです。
スーパーキャット2015

6

問題は、C ++にそのようなタイプがあることです!ですvoid。:-)任意のポインタを安全に暗黙的にキャストできますvoid *基本型へのポインタ、仮想テーブルのないクラス、仮想テーブルのあるクラスなど、できます。

これらすべてのカテゴリのオブジェクトと互換性がある必要があるため、voidそれ自体に仮想メソッドを含めることはできません。仮想関数とRTTIがないと、型に関する有用な情報を取得できませんvoid(すべての型に一致するため、すべての型に当てはまることがわかります)が、仮想関数とRTTIを使用すると、単純型が非常に無効になり、C ++が機能しなくなります。直接メモリアクセスなどの低レベルプログラミングに適した言語。

だから、そのようなタイプがあります。言語の低レベルの性質により、非常に最小限の(実際には空の)インターフェースを提供するだけです。:-)


ポインタタイプとオブジェクトタイプは同じではありません。タイプのオブジェクトを持つことはできませんvoid
ケビンパンコ2013

1
実際、C ++での継承は通常このように機能します。それが行う最も重要なことの1つは、ポインター型と参照型の互換性です。クラスへのポインターが必要な場合は、派生クラスへのポインターを指定できます。また、2つのタイプ間でポインターをstatic_castする機能は、それらが階層で接続されていることを示しています。この観点から、voidは間違いなくC ++のユニバーサル基本クラスです。
エリオ2013

型の変数を宣言すると、voidコンパイルされず、このエラーが発生します。Javaでは、型の変数を持つことができObject、それは機能します。それがvoidと「本物の」タイプの違いです。おそらくそれがvoidすべての基本型であることは事実ですが、コンストラクター、メソッド、またはフィールドを提供しないため、存在するかどうかを判断する方法はありません。この主張は証明も反証もできません。
ケビンパンコ2013

1
Javaでは、すべての変数が参照です。それが違いです。:-)(ところで、C ++には、変数の宣言に使用できない抽象クラスもあります)。そして私の答えでは、なぜRTTIを無効から取得できないのかを説明しました。したがって、理論的にはvoidは依然としてすべての基本クラスです。static_castは、関連するタイプ間でのみキャストを実行し、voidに使用できるため、証明です。しかし、その通りです。無効にRTTIアクセスがないため、高級言語の基本型とは大きく異なります。
エリオ2013

1
@Ellioh C ++ 11標準§3.9.1p9を調べた後、それvoidはタイプであると認めます(そして、いくらか巧妙な使用法を? :発見しました)が、それはまだユニバーサルベースクラスになるにはほど遠いです。一つには「完成できない不完全な型」であり、もう一つにはvoid&型がありません。
bcrist 2015年

-2

C ++は強く型付けされた言語です。それでも、テンプレートの特殊化のコンテキストでユニバーサルオブジェクトタイプがないことは不可解です。

たとえば、パターンを取る

template <class T> class Hook;
template <class ReturnType, class ... ArgTypes>
class Hook<ReturnType (ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

これは次のようにインスタンス化できます

Hook<decltype(some_function)> ...;

ここで、特定の関数に同じものが必要であると仮定しましょう。お気に入り

template <auto fallback> class Hook;
template <auto fallback, class ReturnType, class ... ArgTypes>
class Hook<ReturnType fallback(ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

特殊なインスタンス化で

Hook<some_function> ...

しかし、残念ながら、クラスTは特殊化の前に任意の型(クラスかどうかauto fallback)を表すことができますが、(このコンテキストで最も明白な一般的な非型のものとしてその構文を使用しています)特殊化前の非型テンプレート引数。

したがって、一般に、このパターンは型テンプレート引数から非型テンプレート引数に転送されません。

C ++言語の多くのコーナーと同様に、答えはおそらく「委員会のメンバーはそれについて考えていません」です。


あなたReturnType fallback(ArgTypes...)が(何かのように)うまくいくとしても、それは悪いデザインになるでしょう。template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...あなたが望むことを行い、のテンプレートパラメータHookが一貫した種類
Caleth 2018年

-4

C ++は当初「Cwithclasses」と呼ばれていました。これは、C#のような他のより現代的なものとは異なり、C言語の進歩です。また、C ++を言語としてではなく、言語の基盤として見ることができます(はい、ScottMeyersの著書EffectiveC ++を覚えています)。

C自体は、Cプログラミング言語とそのプリプロセッサの混合言語です。

C ++は別のミックスを追加します。

  • クラス/オブジェクトアプローチ

  • テンプレート

  • STL

私は個人的に、CからC ++に直接来るものが好きではありません。一例は列挙型機能です。C#を使用して開発者が使用できるようにする方法は、はるかに優れています。列挙型を独自のスコープに制限し、Countプロパティを持ち、簡単に反復できます。

C ++はCとの下位互換性を望んでいたため、設計者はC言語全体をC ++に入力できるようにすることを非常に寛容でした(微妙な違いはいくつかありますが、Cコンパイラを使用して実行できることは覚えていません。 C ++コンパイラを使用できませんでした)。


「C ++を言語としてではなく、言語の基盤として見ることはできません」...その奇妙なステートメントが何を推進しているのかを実際に説明して説明することに注意してください。複数のパラダイムは「複数の言語」とは異なりますが、プリプロセッサは残りのコンパイラステップとは素であると言っても過言ではありません。これは良い点です。列挙型-必要に応じてクラススコープで簡単にラップでき、言語全体に内省が欠けています-大きな問題ですが、この場合は概算できます-Boostvaultのbenumコードを参照してください。あなたのポイントはQだけですか:C ++はユニバーサルベースで始まっていませんでしたか?
トニーデルロイ2011

@Tony:不思議なことに、私が言及した本は、別の言語として言及されていない唯一のものはプリプロセッサです。これは、個別に検討する唯一のプリプロセッサです。プリプロセッサが最初に自身の入力を解析するので、私はあなたの見方を理解しています。しかし、テンプレートメカニズムを持つことは、一般化を行う別の言語を持つようなものです。テンプレート構文を適用すると、コンパイラがすべてのタイプの関数を生成します。プログラムをCで記述し、その入力をC ++コンパイラに与えることができる場合、これは2つの言語を1つにまとめたものです。オブジェクトを含むCとC => C ++
sergiol 2011

STLはアドオンと見なすことができますが、テンプレートを介してC ++の能力をネイティブに拡張する非常に実用的なクラスとコンテナーがあります。そして、私が言っていた列挙型は、NATIVE列挙型です。Boostについて話すとき、あなたはその言語にネイティブではないサードパーティのコードに依存しています。C#では、反復可能で自己スコープの列挙型を持つために、外部のものを含める必要はありません。C ++では、私がよく使用するトリックは、列挙型の最後の項目を呼び出すことです-値を割り当てずに、それらは自動的にゼロから開始され、インクリメントされます-NUM_ITEMSのように、これは私に上限を与えます。
sergiol 2011

2
あなたのさらなる説明に感謝します-私は少なくともあなたがどこから来ているのかを見ることができます。テンプレートの使い方を学ぶことは、他のC ++プログラミングとは切り離されていると感じると不満を言う人が何人かいますが、それは確かに私の経験ではありません。私は他の読者にあなたが提示する他の視点について彼らが何をするかを任せます。乾杯。
トニー・デルロイ2011

1
ユニバーサルベースタイプを使用する場合の最大の問題は、仮想メソッドがない場合、そのようなタイプは役に立たないことだと思います。C ++は、仮想メソッドを持たない構造体タイプにオーバーヘッドを追加したくありませんでしたが、仮想メソッドを持つタイプのすべてのオブジェクトには、少なくともディスパッチテーブルへのポインターが必要です。クラスと構造体が異なるユニバースに存在していた場合、ユニバーサルクラスオブジェクトを持つことは可能だったかもしれませんが、クラスと構造体は基本的にC ++では同じものです。
スーパーキャット2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.