トックのCG部屋-トップ別室へ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

菱型継承とshared_ptrとsetでやらかした話

菱形継承したオブジェクトをsetに入れた時に想像してたのと違った挙動をしたのでその話

本編はいつもどおり追記で
菱形継承(ダイヤモンド継承)したオブジェクトのshared_ptrをsetで扱おうとした時におきた問題
簡単に言うとやり方によってはオブジェクトが重複する

やりたかったことは下の図の通り
やりたかったこと
まぁ見るからに胡散臭い話で
胡散臭いものはやっぱりよろしくない
実際にコードにしてみると次の様なことになる

#include <memory>
#include <set>
#include <iostream>

class Super{};
class SubA:public Super{};
class SubB:public Super{};
class SubSub:public SubA,public SubB{};

int main(){

std::set<std::shared_ptr<Super>> set;

auto subsub=std::make_shared<SubSub>();

std::shared_ptr<SubA> subA1=subsub;
std::shared_ptr<SubA> subA2=subsub;
std::shared_ptr<SubB> subB1=subsub;
std::shared_ptr<SubB> subB2=subsub;

set.insert(subA1);
set.insert(subB1);
set.insert(subA2);
set.insert(subB2);

std::cout<<set.size()<<std::endl;

return 0;
}

一度SubA,SubBにキャストしてるのは
そういう引数を取る関数でsetに入れているという想定なのと
そのままだと曖昧さでコンパイルエラーが出るから
まぁこのそのままだとコンパイルエラーが出るってあたりでダメそうって分かれよって話だけど
自分が遭遇したのは前者の状況だったんで気付かなかった
このサンプル書いてる時に知った
そしてこの記事を下の方まで書きながらいろいろ実験するうちに菱形継承が何やってるか理解した

上のプログラムを実行するとsetの要素数が出力されるのだけど
その値は"2"
つまり同じオブジェクトのshared_ptrのはずなのに同じとみなされていない場合がある
もう少し言うと同じ型にキャストした物同士は同じオブジェクトとして扱われ
違う型にキャストした物同士は違うオブジェクトとしてみなされる

なぜそういうことがおこるのか
まぁC++の菱型継承を理解してる人にとっては当たり前なんだろうけど
確かにクラスダイアグラムでは菱形になっているんだけど
実体としてはSubA,SubBそれぞれがSuperを持っている
だからキャストによってSubAを経由するSuperとSubBを経由するSuperは別物になる

とかごちゃごちゃ書きながら調べてたんだけど
仮想継承とかあるんだね!知らなかった!

class Super{};
class SubA:virtual public Super{};
class SubB:virtual public Super{};
class SubSub:public SubA,public SubB{};

これで解決するよ!
まじで馬鹿じゃないのか……何やってたんだよ……

仮想継承すると菱形継承でもちゃんとSuperが統一されるからちゃんとsetに重複なしで入る
ちなみに上で言っている曖昧さも仮想継承じゃないとSuperが2つできるせいだから
仮想継承使えば一度どっちかにキャストとか意味のわからないことやらなくても普通に直接入れられる

後まぁやっぱり菱形継承はなんか胡散臭いから
インタフェース以外ではあんまり使わない方がいいと思う
  1. 2014/07/14(月) 19:49:35|
  2. 雑記
  3. | トラックバック:0
  4. | コメント:0
<<空母モデリングの続き | ホーム | アバター的なキャラアイコン更新>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://took.blog72.fc2.com/tb.php/857-69defb33
この記事にトラックバックする(FC2ブログユーザー)

プロフィール

Author:トック

プロフィール(仮)

twitter:elgraiv_took
└ブログ更新情報

twitter:elgraiv_take
└無駄な日常つぶやき用

FC2カウンター

コンテンツ一覧

本棚

最近の記事

カテゴリー

月別アーカイブ

ブログ内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。