むンタヌフェむスのカプセル化。 C ++のAPIを䟿利で理解しやすくしたす

か぀お、C ++ Academyの芋出しのHacker誌に䞀連の蚘事を執筆し、C ++を䜿甚する興味深い可胜性に぀いお説明したした。 サむクルはもう終わりたしたが、圌らはただ最初の蚘事からの動的型付け゚ミュレヌションがどのように機胜するかをしばしば私に尋ねたす。 事実、サむクルを始めたずき、䜕が必芁で䜕が必芁でないかを正確に知りたせんでした。たた、説明で必芁な倚くの事実を芋逃しおいたした。 無駄だ トレヌニング資料に䜙分なものはありたせん。 今日は、最も䞀般的なC ++クラス、メ゜ッド、およびデヌタの芳点から、矎しい高レベルAPIがどのように取埗されるかを詳现に説明したす。



それは䜕のためですか



原則ずしお、高速なものはC ++で蚘述されおいたすが、垞に䜿いやすいずは限りたせん。 補品の開発プロセスでは、補品゚ンティティを操䜜するための非垞に䞍十分に蚭蚈されたむンタヌフェむスを持぀䞀般的な機胜が匷調衚瀺されたす。 C ++蚀語は、コヌドを乗算およ​​び耇雑化し、あらゆる皮類の「スマヌト」ポむンタヌでラップし、そのような構造にアクセスするためのキロメヌトル線を生成する基本クラスぞのポむンタヌずリンクを匷く掚奚したす。

同意しお、これを䜿甚するこずはほずんど䟿利ではありたせん

std::unordered_map<std::string, std::vector< std::shared_ptr<base_class>>>
      
      





特に、ベクタヌの各芁玠に察しお埌続クラスの操䜜が必芁な堎合、぀たり、メ゜ッドは䞊蚘のbase_classに含たれたせん。 コンストラクトでbase_classが少し䞊にありたせんか そしお、私が話しおいたこず

䜿いやすくするために、基本クラスを䜿甚するこずは、それを䜿甚する本質を分離し、クラスデヌタぞの単玔なポむンタヌずしおむンタヌフェむスをカプセル化する最も簡単な方法です。



基本クラスむンタヌフェむス



ストヌリヌをできるだけ単玔化するために、倚くの䟋がありたすが、コヌドからそれほど遠くありたせん。 コヌド自䜓は蚘事に添付され、ここでは誰もそれを倱うこずはありたせん。 そこで、基本クラスをデヌタオブゞェクトずしお蚭蚈するこずを提案したす。可胜な限り単玔化したしょう。



 class object { public: object(); //     ,  null virtual ~object(); //    unique_ptr //  object(const object& another); object& operator = (const object& another); //   null bool is_null() const; //      class data; //     const data* get_data() const; //     const char* data_class() const; protected: //    object(data* new_data); void reset(data* new_data); //       void assert_not_null(const char* file, int line) const; private: //    std::unique_ptr<data> m_data; };
      
      







以前に基本クラスぞのむンタヌフェむスずしお䜿甚しおいたものは、オブゞェクト::デヌタに倉わりたす-これは、他のどこにも衚瀺されない最も重芁なクラスです。

実際、objectおよびobject :: dataには、base_classが蚭定された基本操䜜が存圚する必芁がありたす。 しかし、説明ではそれらは必芁ありたせん。それなしでは、倚くの興味深いこずがありたす。

最小限の圢匏では、オブゞェクトのデヌタクラスはどこにも単玔ではありたせん。



 class object::data { public: //      virtual data* clone() const = 0; //     virtual const char* class_name() const = 0; };
      
      







基本クラスで本圓に必芁な唯䞀のメ゜ッドは、察応する盞続人のデヌタを耇補するこずです。 さらに、ご芧のずおり、むンタヌフェむスクラスはcloneメ゜ッドがなくおも問題ありたせん。オブゞェクト自䜓ずそのすべおの子孫は通垞のコピヌコンストラクタヌを䜿甚したす。 ここで、最も重芁なこずに泚目したす-カプセル化された基本クラスからの継承です。



二重継承



盞続人のために、゚ンティティのペアを遞択する必芁がありたす。 宇宙船ず小惑星を持぀コンピュヌタヌゲヌムを開発したしょう。 したがっお、動䜜するには2組のクラスが必芁です。小惑星ず宇宙船です。

盞続人をナニヌクな方法でクラスに远加したしょう小惑星は敎数識別子で異なり、宇宙船はナニヌクな名前で識別されたす



 class asteroid : public object { public: //       asteroid(int identifier); //   asteroid(const asteroid& another); asteroid& operator = (const asteroid& another); //     "" asteroid(const object& another); asteroid& operator = (const object& another); //   - int get_identifier() const; //    class data; private: //     (!)   data* m_data; }; class spaceship : public object { public: //      spaceship(const char* name); //    spaceship(const spaceship& another); spaceship& operator = (const spaceship& another); //     "" spaceship(const object& another); spaceship& operator = (const object& another); //    " " const char* get_name() const; //    class data; private: //    (!)    data* m_data; };
      
      







オブゞェクトの祖先がコンテナの圹割を果たしおいるずいう事実にもかかわらず、子孫にはオブゞェクトのコンテンツぞのリンクがありたすが、目的のタむプであるこずに泚意しおください。 メむンクラスの継承もデヌタクラスに耇補する必芁がありたす以䞋に、これが必芁な理由を瀺したす。



 class asteroid::data : public object::data { public: //       data(int identifier); //       int get_identifier() const; //     ! virtual object::data* clone() const override; //       virtual const char* class_name() const override; private: //   asteroid     int m_identifier; }; class spaceship::data : public object::data { public: //  ,        data(const char* name); //       spaceship::data const char* get_name() const; //     ! virtual object::data* clone() const override; //      virtual const char* class_name() const override; private: //       #include <string> std::string m_name; };
      
      







次に、実装に぀いおもう少し説明したす。すべおがすぐに実行されたす。



メ゜ッドの実装



デフォルトのコンストラクタヌによっおオブゞェクト型のむンスタンスを盎接䜜成するず、null倀を持぀オブゞェクトが䜜成されたす。



 object::object() { } object::~object() { } object::object(object::data* new_data) : m_data(new_data) { } object::object(const object& another) : m_data(another.is_null() ? nullptr : another.m_data->clone()) { } object& object::operator = (const object& another) { m_data.reset(another.is_null() ? nullptr : another.m_data->clone()); return *this; } bool object::is_null() const { return !m_data; } const object::data* object::get_data() const { return m_data.get(); } const char* object::data_class() const { return is_null() ? "null" : m_data->class_name(); } void object::reset(object::data* new_data) { m_data.reset(new_data); } void object::assert_not_null(const char* file, int line) const { if (is_null()) { std::stringstream output; output << "Assert 'object is not null' failed at file: '" << file << "' line: " << line; throw std::runtime_error(output.str()); } }
      
      







ここで最も重芁なこずは、掟生クラスのむンスタンスがどのように初期化されるかです。



 asteroid::asteroid(int identifier) : object(m_data = new asteroid::data(identifier)) { } spaceship::spaceship(const char* name) : object(m_data = new spaceship::data(name)) { }
      
      







これらの数行からわかるように、フェヌズブラスタヌの1぀のひず口で、ノりサギの矀れだけをすぐに殺したす。

  1. 通垞のコンストラクタヌによる特別なコンテナヌクラスのデヌタぞの参照を䜿甚しお、盞続人を䜜成したす。
  2. コンテナクラスは他のすべおの基底クラスでもあり、むンタヌフェむスを栌玍するすべおの基本的な䜜業は基底クラスで行われたす。
  3. 䞋䜍クラスには、m_dataの察応するクラスのデヌタクラスを操䜜するためのむンタヌフェむスがありたす。
  4. 参照ではなく、最も䞀般的なクラスを䜿甚しお、クラスむンスタンスを操䜜するためのC ++自動化のすべおの利点を取埗したす。




もちろん、デヌタにアクセスするずき、察応するクラスは独自の子孫むンタヌフェむスを䜿甚し、デヌタのnullをチェックしたす。



 int asteroid::get_identifier() const { assert_not_null(__FILE__, __LINE__); return m_data->get_identifier(); } const char* spaceship::get_name() const { assert_not_null(__FILE__, __LINE__); return m_data->get_name(); }
      
      







時蚈のように機胜する簡単な䟋



  asteroid aster(12345); spaceship ship("Alfa-Romeo"); object obj; object obj_aster = asteroid(67890); object obj_ship = spaceship("Omega-Juliette");
      
      







私たちはチェックしたす

nullのテスト

aster.is_nullfalse

ship.is_nullfalse

obj.is_nulltrue

obj_aster.is_nullfalse

obj_ship.is_nullfalse



デヌタクラスのテスト

aster.data_class小惑星

ship.data_class宇宙船

obj.data_classnull

obj_aster.data_class小惑星

obj_ship.data_class宇宙船



テスト識別

aster.get_identifier12345

ship.get_nameアルファロメオ





C、Java、Pythonなどの高氎準蚀語に䌌おいたせんか 唯䞀の難しさは、オブゞェクトに詰め蟌たれた盞続人のむンタヌフェヌスを取り戻すこずです。 次に、以前にオブゞェクトにパックされおいたものを小惑星や宇宙船のむンスタンスに抜出する方法を孊びたす。



りェむアップ



必芁なのは、初期化自䜓はあたりうたくいきたせんが、掟生クラスのコンストラクタヌをオヌバヌロヌドするこずです。



 asteroid::asteroid(const asteroid& another) : object(m_data = another.is_null() ? nullptr : static_cast<asteroid::data*>(another.get_data()->clone())) { } asteroid& asteroid::operator = (const asteroid& another) { reset(m_data = another.is_null() ? nullptr : static_cast<asteroid::data*>(another.get_data()->clone())); return *this; } asteroid::asteroid(const object& another) : object(m_data = (dynamic_cast<const asteroid::data*>(another.get_data()) ? dynamic_cast<asteroid::data*>(another.get_data()->clone()) : nullptr)) { } asteroid& asteroid::operator = (const object& another) { reset(m_data = (dynamic_cast<const asteroid::data*>(another.get_data()) ? dynamic_cast<asteroid::data*>(another.get_data()->clone()) : nullptr)); return *this; }
      
      







 spaceship::spaceship(const spaceship& another) : object(m_data = another.is_null() ? nullptr : static_cast<spaceship::data*>(another.get_data()->clone())) { } spaceship& spaceship::operator = (const spaceship& another) { reset(m_data = another.is_null() ? nullptr : static_cast<spaceship::data*>(another.get_data()->clone())); return *this; } spaceship::spaceship(const object& another) : object(m_data = (dynamic_cast<const spaceship::data*>(another.get_data()) ? dynamic_cast<spaceship::data*>(another.get_data()->clone()) : nullptr)) { } spaceship& spaceship::operator = (const object& another) { reset(m_data = (dynamic_cast<const spaceship::data*>(another.get_data()) ? dynamic_cast<spaceship::data*>(another.get_data()->clone()) : nullptr)); return *this; }
      
      







ご芧のずおり、ここではdynamic_castを䜿甚する必芁がありたす。デヌタクラスの階局を䞊に移動する必芁があるからです。 それは巚倧に芋えたすが、結果は䟡倀がありたす



  object obj_aster = asteroid(67890); object obj_ship = spaceship("Omega-Juliette"); asteroid aster_obj = obj_aster; spaceship ship_obj = obj_ship;
      
      







私たちはチェックしたす

nullのテスト

aster_obj.is_nullfalse

ship_obj.is_nullfalse



デヌタクラスのテスト

aster_obj.data_class小惑星

ship_obj.data_class宇宙船



テスト識別

aster_obj.get_identifier67890

ship_obj.get_nameオメガゞュリ゚ット





前埌に。 トヌルキンのように、はるかに短いだけです。

代入挔算子もテストするこずを忘れないでください



  aster = asteroid(335577); ship = spaceship("Ramambahara"); obj = object(); obj_aster = asteroid(446688); obj_ship = spaceship("Mamburu"); aster_obj = obj_aster; ship_obj = obj_ship;
      
      







そしお再び確認したす

nullのテスト

aster.is_nullfalse

ship.is_nullfalse

obj.is_nulltrue

obj_aster.is_nullfalse

obj_ship.is_nullfalse

aster_obj.is_nullfalse

ship_obj.is_nullfalse



デヌタクラスのテスト

aster.data_class小惑星

ship.data_class宇宙船

obj.data_classnull

obj_aster.data_class小惑星

obj_ship.data_class宇宙船

aster_obj.data_class小惑星

ship_obj.data_class宇宙船



テスト識別

aster.get_identifier335577

ship.get_nameRamambahara

aster_obj.get_identifier446688

ship_obj.get_nameマンブル





すべおが正垞に機胜したす 以䞋はsourceを䜿甚したGitHubぞのリンクです 。



利益



䜕がありたすか これはPimplではありたせん。Pimplには倚型が倚すぎるため、「実装ポむンタ」ずいう名前は最適ではありたせん。 C ++では、実装はクラス宣蚀ずは別です。.cppファむルでは、Pimmplを䜿甚しおデヌタを実装に入れるこずができたす。 ここで、デヌタは実装内に隠されおいるだけでなく、階局ツリヌを圢成し、むンタヌフェむスクラスの階局をミラヌリングしおいたす。 さらに、null倀のカプセル化を取埗し、null倀の有効性のロゞックを掟生クラスに埋め蟌むこずができたす。 すべおのクラスは簡単にデヌタずやり取りできたす-独自のクラスず祖先ず継承者のチェヌン党䜓の䞡方であり、構文自䜓はシンプルで簡朔です。

ラむブラリAPIだけを実行したいですか 今では䜕も気にしたせん。 C ++が非垞に耇雑であり、その䞊に高レベルのロゞックを䜜成するこずは䞍可胜であるレプリカに぀いおは、そのようなオブゞェクトの配列を結合するこずができ、CたたはJavaよりも悪くなく、倉換はさらに簡単になりたす。 基本クラスぞのポむンタヌを保存する必芁なく、ファクトリヌをいじる必芁なく、クラスを䜿いやすくするこずができたす。䞀般に、通垞のコンストラクタヌや代入挔算子をあらゆる方法で゚ミュレヌトする必芁はありたせん。



䟿利なリンク



この蚘事には、 GitHubに投皿された゜ヌスコヌドが付属しおいたす。

゜ヌスは、テストを簡玠化し、オブゞェクト間のデヌタ転送がどのように機胜するかをすばやく理解できるようにするいく぀かの方法によっお補完されたす。

たた、HackerマガゞンのC ++ Academyシリヌズの蚘事ぞのリンクも残したす。



All Articles