Exam05: Polyset (Lv1)
C++におけるデータ構造の実装と、多重継承(ダイヤモンド継承)を用いたインターフェースの拡張を学ぶ課題です。
課題概要
「バッグ(Bag / Multiset)」とは、重複を許容するコレクションです。この課題では、配列と二分木を用いてバッグを実装し、さらに「検索機能(has)」を追加し、最終的に一意性を保証する「セット(Set)」ラッパーを作成します。
主要技術の深掘り
1. 仮想継承 (Virtual Inheritance)
searchable_bag が bag を継承する際、および searchable_array_bag が両方を継承する際、bag の実体が重複しないように virtual public bag として継承する必要があります。
2. Set(ラッパー)
set クラスは継承階層には含まれず、searchable_bag の参照をメンバとして持ちます(Aggregation)。insert 時に has() で存在確認を行うことで、一意性を保証します。
ロジックの可視化
クラス図
この課題の核心は、ダイヤモンド継承(Diamond Inheritance)の理解と実装にあります。
コラム・読み物
モダンC++の設計思想や、技術的な詳細についての深掘り記事です。
よくある質問 (FAQ)
Q. has(), print() などをヘッダーに書いてもいい?
A. 短い転送関数(Forwarding function)やゲッターであれば、インライン化の恩恵を受けられるため推奨されます。ただし、複雑なロジックは .cpp に書くべきです。
Q. get_bag() に const 版と非 const 版があるのはなぜ?
A. const な set オブジェクトからは読み取り専用の bag を、変更可能な set オブジェクトからは変更可能な bag を返すためです。これにより const 正確性(Const Correctness)が保たれます。
Q. set の代入演算子はどうすべき?
A. set は参照(searchable_bag&)を持っていますが、参照先そのものを変えることはできません。代入演算子では、コピー元のバッグの中身をコピー先のバッグに反映させる(clear() して insert し直すなど)実装が一般的です。あるいは、代入を禁止(= delete)するのも一つの手です。
テストと検証 (main.cpp)
プログラムのエントリポイントとなる main.cpp は、以下の動作を確認するためのテストドライバです。
- 多態性:
searchable_array_bagもsearchable_tree_bagも、同じインターフェースで扱えること。 - 一意性:
setラッパー経由で挿入した場合、重複した値が無視されること。 - メモリ管理:
valgrindやLeaksでメモリリークがないこと。