3

私はC++を学んでいて、カスタム文字列クラスを実装して、文字列の反転、大文字と小文字の変換、変換などMyTextProc::Wordの機能を追加したいと考えています。std::string

これは、is-a関係を使用して行うのが最適なようです。

namespace MyTextProc {
   class Word : public string {
      /* my custom methods.... */
   };
}

クラスにコンストラクターを指定していませんが、上記のWordクラスの定義では、デフォルトのvoidコンストラクターとコピーコンストラクターのみが公開Wordされています。すべてのパブリック文字列コンストラクターも継承できませんか?

Wordのように作業するのが良いでしょうstring。文字列にプロパティを追加していません。新しいサブクラスを実装するには、文字列のすべてのコンストラクターである基本クラスを実際に実装する必要がありますか?

これは、has-a関係を使用して行うのが最適ですか?コンストラクターを実装し、const string&クライアントに構築のために文字列オブジェクトを渡すように要求する必要がありますか?すべての文字列コンストラクターをオーバーライドする必要がありますか?

4

2 に答える 2

3

C++地獄へようこそ。

C ++の最も物議を醸す側面の1つに触れたばかりです。std::stringはポリモーフィックではなく、コンストラクターは継承されません。

唯一の「クリーンな」方法(これはいかなる種類の批判もしません)は、std :: stringをメンバーとして埋め込み、そのすべてのメソッドを委任することです。よくできました!

他の方法も考えられますが、常にいくつかの制限に注意する必要があります。

  • std :: stringには仮想メソッドがないため、それを派生させても、ポリモーフィック型は取得されません。
  • つまり、yoyがWordを刺し傷保持関数に渡し、その関数が文字列メソッドを呼び出す場合、オーバーライドメソッドは呼び出されず、
  • Word viaの割り当てをnew文字列に指定してはなりません*:そのようなポインタを介して削除すると、未定義の動作が発生します
  • stringを受け取りstring-sを返す継承されたメソッドはすべて機能しますが、Wordではなくstringを返します。

コンストラクターについては、継承されません。デフォルトの構造継承は幻想です。これは、コンパイラがデフォルトのデフォルト実装を合成し、コピーして割り当て、暗黙的にベースを呼び出すためです。

C ++ 11では、回避策は次のようになります。

class Word: public std::string
{
public:
    template<class... Args>
    Word(Args&&... args) :std::string(std::forward<Args>(args)...) 
    {}

    //whatever else   
};

これにより、適切なstd :: sting ctorの呼び出しで指定されるあらゆる種類の引数が作成されます(存在する場合は、コンパイルエラーが発生します)。

次に、デザインを自分で決定します。通常のstd::stringと独立した無料関数のセットが付属している可能性があります。

別の(不完全な)方法は、Wordを継承せずに、std :: stringを埋め込み、上記のように構築し、暗黙的にstd::stringに変換できるようにすることです。(さらに、str()明示的なメソッドがあります)。これにより、Wordを文字列として使用したり、文字列からWordを作成したりできますが、文字列の「代わりに」Wordを使用することはできません。

学ぶべきもう1つのこと(Javaからの可能性があります...):「is-a=継承およびhas-a=埋め込み」OOPルールに自分自身を固定しないでください。すべてのC++標準ライブラリオブジェクトはOOPの意味でのオブジェクトではないため、すべてのOOPスクールの方法論にはその文脈での誤謬があります。

単純なコーディング(および「自分自身を繰り返さない」パラダイムの適切な適用、継承によりはるかに簡単)と単純なメンテナンス(および埋め込みによりコードが使用されにくくなる)の間のトレードオフを決定する必要があります。他人によって間違って)


これは、以下のコメントに対する回答です。

標準のC++クラスのポリモーフィズムの欠如。これはなぜですか?仮想関数を使用してstd C ++ libsを実装しないことは、それらが強化するように設計された言語のポイントを打ち負かしているようです!!!「」

ええと...はい、そうではありません!

PERLを引用しているので、次のことを考慮してください。PERLはスクリプト言語であり、型は動的です。-Javaは、型が静的でオブジェクトが動的である言語です-C ++は、型が静的でオブジェクトが静的である言語です(オブジェクトの動的割り当ては明示的です)。

現在、Javaでは、オブジェクトは常に動的に割り当てられ、ローカル変数はそれらのオブジェクトへの「参照」です。C ++では、ローカル変数はそれ自体がオブジェクトであり、値のセマンティクスを持っています。また、C ++標準ライブラリは、拡張するベースのセットとしてではなく、テンプレートを使用してコードを生成する値型のセットとして設計されています。

std::stringは、動作と同じように機能するものと考えてintください。intから派生して、「より多くのメソッド」を取得したり、一部のメソッドの動作を変更したりすることを期待していますか?

ここでの論争の的となる側面は、-この設計と一貫性を保つために-std :: stringはその内部メモリを管理するだけで、メソッドを持たないようにする必要があるということです。代わりに、文字列関数sholdはテンプレートとして実装されているため、同じstd::string外部動作を示す他のクラスで「アルゴリズム」として使用できます。デザイナーがしなかったこと。

彼らはそれに多くのメソッドを配置しましたが、値のセマンティクスを保持することを多形にしないため、設計があいまいになり、それらのメソッドを再宣言せずに「再利用」する唯一の方法を継承に保持します。これは可能ですが、私があなたに言った制限があります。

新しい機能を効果的に作成したい場合、「価値のポリモーフィズム」を持たせるには、teplatesを使用します。

std::string capitalize(const std::string& s) { .... }

次のようなことをします

template<class String>
String capitalize(const String& s) { .... }

これにより、どのタイプの文字でも、文字に関して同じ文字列インターフェイスを持つクラスをコーディングできます。

于 2013-02-24T18:34:11.027 に答える
2

正直なアドバイスとして、文字列を受け取り、文字列を返す関数として、必要なメソッドを実装します。それらは、テスト、分離、および使用がより簡単になります。C ++の場合、関数が実行するときに常にクラスに到達するとは限りません。実際、テンプレートに入ると、基本的な文字列クラスの定義と特殊化なしでテンプレート化された関数を作成できます。そうすれば、触れている文字列型にカスタム定義のメソッドがあるかどうかを常に知ることができます(もちろん、Microsoftと対話すると、5,000万の文字列実装があることがわかります)。

于 2013-02-24T18:14:06.817 に答える