[[C++ Tips]]

* 参照型のメンバをもつクラスのコンテナとソート [#j00f0661]

** 参照型のインスタンスをもつクラスは、通常だとvector等のコンテナに格納できない。 [#k54c94c3]

  // 参照型のインスタスをもつクラス
  struct Hoge {
    int& instance_;
    Hoge(int& i) : instance_(i) {}
    Hoge(const Hoge& h) : instance_(h.instance_) {}
    bool operator < (const Hoge& h) const { return instance_ < h.instance_; }
  };
std::vectorに入れてみる
  std::vector<Hoge> vectorhoge;
  int h1 = 1;
  vectorhoge.push_back(h); // コンパイルエラー
operator =がないとvector::push_backはできない             

listならOK
  std::list<Hoge> listhoge;
  int h1 = 1;
  listhoge.push_back(h); // OK

** vectorに格納可能なリファレンスをもつクラスの定義 [#vea9f3c7]

- vectorに格納(push_back, back_insert_iteratorなど)には、operator = が必要
- リファレンス型の変数は、リファレンス情報の更新はできない。代入もswapも参照先のデータにたいして行われる。

*** 禁断の技その1 [#m5f8dfc7]

 Hoge operator = (const Hoge& h) {
    memcpy(this, &h, sizeof(Hoge));
    return *this;
 }

+ thisのデストラクタが呼ばれないで破棄される
+ オブジェクトのバイナリーコピーは非常に危険なのでダメ

*** 禁断の技その2 [#f178b88a]

 Hoge operator = (const Hoge& h) {
   this->~Hoge();
   new (this) Hoge(h);
   return *this;
 }
+ いちおう、安全。
+ いちおう、言語的には安全。
+ ハーブ・サッターさんは絶対に使うなと警告している方法
+ コピーコンストラクタとデストラクタで例外を投げないことが前提。例外が発生すると破綻する。
+ とりあえず、これでvectorに対する要素の追加やsortが可能になる
+ スレッドセーフでもない。シングルスレッド限定。
+ 自己代入の処理も必要
+ 欠点は多いけど、これでvectorに対する要素の追加やsortが可能になる

*** コードの例 [#s7418473]
 #include <cstdio> 
 #include <list>
 #include <vector>
 #include <algorithm>
 #include <functional>
 #include <iostream>
  
 // 参照を持つクラスのswap
 namespace {
 
  struct Hoge {
     int& instance_;
     Hoge(int& i) : instance_(i)
     {}
     Hoge(const Hoge& h) : instance_(h.instance_) {}
 
     // コピーコンストラクタとnewの第二構文を使った初期化を使って実現する。
     // 例外安全ではない。コピーコンストラクタが例外を投げないことが前提
     // 例外安全ではない。デストラクタ・コピーコンストラクタが例外を投げない
     // スレッドセーフではない。スレッドセーフにするには排他処理が必要。
     Hoge& operator = (const Hoge& h) {
       this->~Hoge();       // thisを破棄
       new(this) Hoge(h);
       if (this != &h) { // 自己代入でない
         this->~Hoge();       // thisを破棄 デストラクタは例外を投げない
         // ここで他のスレッドがthisを参照すると破綻する。シングルスレッド限定!
         try {
           new(this) Hoge(h);
         }
         catch (...) {
           assert(false); // 例外が投げられたら停止
         }
       }
       return *this;
     }
 
     // リファレンスタイプのインスタンスは通常の方法ではswapできないので、
     // コピーコンストラクタとnewの第二構文を使った代入の=を使って実現する。
     // 例外安全ではない。コピーコンストラクタが例外を投げないことが前提
     void swap(Hoge& h) {
       Hoge tmp(h);         // hをコピー
       h = *this;
       *this = tmp;
     }
 
     bool operator < (const Hoge& h) const { return instance_ < h.instance_; }
 
   };
 
   // output用
   void print(const Hoge& h) { printf("%d\n", h.instance_); }
 
 }
 
         
 int main(int argc, char* argv[])
 {
   int i1 = 1;
   int i2 = 2;
   int i3 = 3;
   Hoge h1(i1), h2(i2), h3(i3);
   std::vector<Hoge> vectorhoge;  
   vectorhoge.push_back(h3); // operator =がないとvector::push_backはでき ない 
   vectorhoge.push_back(h2);
   vectorhoge.push_back(h1);
   std::back_inserter(vectorhoge) = h1;
   std::sort(vectorhoge.begin(), vectorhoge.end()); // operator =がないと vector::sortはできない
   std::cout << "vector sort" << std::endl;
   std::for_each(vectorhoge.begin(), vectorhoge.end(), print);
 
   std::list<Hoge> listhoge;
   listhoge.push_back(h3);
   listhoge.push_back(h2);
   listhoge.push_back(h1);
   std::back_inserter(listhoge) = h1;
   listhoge.sort();
   std::cout << "list sort" << std::endl;
   std::for_each(listhoge.begin(), listhoge.end(), print);
 
   return 0;
 }


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS