[[C++ Tips]]

* 普通の配列をboost:arrayに化けさせる [#wd8b9f45]

配列をboost::arrayやstd::vectorに置き換えてしまえばよいのですが、既存のコードを修正するのも大変なので、
既存の配列をboost::arrayに見せかけるツールを作りました。

 template <typename T, size_t N>
 inline boost::array<T,N>& to_array(T(&ar)[N]) {
         return *reinterpret_cast<boost::array<T,N>*>(&ar[0]);
 }
 template <typename T, size_t N>
 inline const boost::array<T,N>& to_array(const T(&ar)[N]) {
         return *reinterpret_cast<const boost::array<T,N>*>(&ar[0]);
 }

to_array(<配列変数>); で、boost::array<型,サイズ> に変換します。(無理やりキャストしているだけ)
これで、生の配列変数に対して、iteratorとか、size()とか、stl的な機能を使うことができます。


ideone: http://ideone.com/oi43Mo

* 静的な文字列リテラルは、同名のものは同じアドレスを示す [#tc41e4a9]

プログラム中に"hoge"のように記載する文字列リテラルは、リンク時に静的な領域にまとめられ、同一の文字列は一つにまとめられます。(ビルドオプションで設定)
それを利用して、静的リテラルのアドレスをハッシュ値として扱うことができます。

 #include <iostream> 
 
 const char* s1 = "hoge";
 const char* s2 = "hoge"; 
 
 int main(int ac, char*av[]) { 
 	const char* s3 = "hoge";
 	std::cout << "s1 == s2     : " << (s1 == s2) << std::endl;
 	std::cout << "s1 == s3     : " << (s1 == s3) << std::endl;
 	std::cout << "s1 == 'hoge' : " << (s1 == "hoge") << std::endl;
 } 

output
 s1 == s2     : 1
 s1 == s3     : 1
 s1 == 'hoge' : 1

http://ideone.com/fJz5e

* 整数型配列の初期化 [#e87c5d4b]

クラスインスタンスの整数型配列をゼロで初期化するには、初期化リストに記載するだけでよい。

before
 class Hoge {
   int a[10];
   hoge() {
     memset(&a[0], 0, sizeof(a));
   }
 }:

after
 class Hoge {
   int a[10];
   hoge() : a() {
   }
 }; 


http://ideone.com/E3rEc

* 多重継承したクラスをnewの配置構文で確保したときの落とし穴 [#tc8abb64]

多重継承したクラスのポインタは、親クラスのポインタにキャストした場合、アドレスが同一にならない。

 class C : public A, public B {...}
 C* c = new C;
 assert((A*)c == (B*)c); // assertで停止

ポインタの値で比較するときは注意が必要。

同じ理由で、newの配置構文でクラスインスタンスを確保した場合、親クラスのポインタで削除すると不具合が発生する場合がある。

 class C : public A, public B {...}
 void* buffer = malloc(sizeof(C));
 C* c = new(buffer) C;
 B* b = c; // B*に代入
 b->~B();  // ~B()がvirtualならば、デストラクタはA,Cとも正常に呼び出される
 free(b); // bの値はbufferと異なるので死にます

↓ ideone.comでのサンプル~
http://ideone.com/LdCNi

* == true で比較してはいけない理由 [#jcd6c4c2]

if文で値をbooleanとして比較するとき、trueと==で比較すると、思わぬ落とし穴にはまることがある。

※ C++ では、intとbool値と比較しても、左辺値に対して暗黙の型変換は行われない。

 #include <stdio.h>
 int main(int ac, char* av[]) {
     int a = 2;
     if (a)          printf("a is true\n"); else printf("a is false\n");
     if (a == true)  printf("a is true\n"); else printf("a is false\n");
     if (a != false) printf("a is true\n"); else printf("a is false\n");
     if (static_cast<bool>(a) == true)
                     printf("a is true\n"); else printf("a is false\n");
     return 0;
 }       

結果
 a is true
 a is false
 a is true
 a is true

"a == true" で比較したときだけ、結果が異なる。

http://ideone.com/UbvzC


* 仮想関数の部分オーバーライドについて [#wc903ad4]

 class Base {
 public:
   virtual void hoge() = 0;
   virtual void hoge(int a) { printf("a=%d\n", a); }
 };
 class Sub : public Base {
 public:
   virtual void hoge() { printf("hoge\n"); }
 };

このように仮想関数を部分的にオーバーライドした場合、警告がでる。~
回避するには、 class Subに、"using Base::hoge; "を加えるとよい。

本来なら同名のオーバーライドを避けるべきである。

http://stackoverflow.com/questions/2057823/issues-with-partial-class-function-overrides-in-c

* #defineマクロとの戦い [#n3d01c1c]

 #define hoge(x) HOGE(x)

などで、別名を無理やり定義されてしまっているのをすり抜ける方法。

- 方法1~
(ns::function) のようにnamespace付きで()で囲む~
例
 (std::printf)("hoge") ; // printfが#defineされていてもオリジナルが呼ばれる

- 方法2~
関数名と引数リストの間に空の#defineを挟む~
例
 #define FOO
 printf FOO("hoge"); // printfが#defineされていてもオリジナルが呼ばれる


** コード例 (http://ideone.com/S44K6) [#gaad9cff]
 #include <stdio.h>
  
 namespace foo {
 void hoge(const char* const msg) { printf("small hoge : %s\n", msg); }
 void HOGE(const char* const msg) { printf("LARGE HOGE : %s\n", msg); }
 }
  
 #define hoge(msg) HOGE(msg)
 #define FOO
  
 int main(int ac, char* av[]) {
         foo::hoge("ns::function");
         (foo::hoge)("(ns::function)");
         foo::hoge FOO("ns::function FOO");
 };

output
 LARGE HOGE : ns::function
 small hoge : (ns::function)
 small hoge : ns::function FOO
 
 

* boost pool [#s2f524fb]

- boost poolには、pool, object_pool, singleton_poolの3種類のタイプがある

- pool
-- malloc/freeを行う
-- コンストラクタでサイズを指定
-- コンストラクタで指定サイズのメモリをアロケート/フリーできる
-- 指定サイズのN倍も可能 (ordered)
-- orderedと通常がある。

- object_pool
-- クラスのインスタンスを生成/破棄する
-- orderedのみ

- singleton_pool

--サイズ別にシングルトンとして動作する。
--シングルトンとして使える。
--vectorなどSTLのコンテナのアロケーターとして使える
--メモリの解放にはpurge_memoryしかない。(全放棄)

** orderedとは [#vc6ee3bd]

- 必ずメモリアドレスが順番になる(連続する)
- orederdだと、破棄したメモリブロックの再利用はされない
- 解放するには、release_memoryを呼ぶ(アロケートブロックが解放される)
-非orderedが一つでも混在していると、release_memoryができない
- おそい

**通常版(非orederd) [#ha2b1cac]
-破棄したメモリブロックが再利用される
-release_memoryはできない
-全部破棄のpurge_memoryは可
-連続したメモリブロックを確保できない

*** ordered、非orderedともに一長一短で使いにくい [#r8f6dbcf]


* オーバーライドした関数の戻り値は、基本クラスの定義の戻り値を継承したものでもOK [#rdf3f287]

 #include <stdio.h>
  
 struct Base {
         virtual Base* create() = 0; // create new instance
         virtual Base* clone() = 0;  // create and copy instance
         virtual void print() = 0;
 };
  
 struct Sub : Base {
         Sub() {}
         Sub(const Sub& sub) {} // copy constructor
         Sub* create() { return new Sub(); }
         Sub* clone() { return new Sub(*this); }
         void print() { printf("I am Sub\n"); }
 };
  
 int main(int ac, char* av[]) {
         Base* base1 = Sub().create();
         Base* base2 = base1->clone();
         base2->print();
         return 0;
 }
  
- 実行結果~
http://t.co/ASDjAEvI

* boost:multiindexは便利 [#md6c228e]

http://d.hatena.ne.jp/faith_and_brave/20091126/1259226643

* virtualやポインタを使わず、サブクラスの生オブジェクトをコンテナに入れるためのクラス [#d948f293]

    // BaseClassのサブクラスをコンテナにいれるためのクラス
    // このクラスを使うことで、ポインタを使わず動的ポリモーフィズムを実現する
    template <typename BaseClass, size_t MaxSize>
    struct DynamicPolymorphism : BaseClass {
        uint8_t body[MaxSize - sizeof(BaseClass)];
        DynamicPolymorphism() : BaseClass() {}
        DynamicPolymorphism(const DynamicPolymorphism& m) : BaseClass(m) {
            memcpy(body, m.body, sizeof(body));
        }
        template <typename SubClass>
        DynamicPolymorphism(const SubClass& m) : BaseClass(m) {
            SubClass& self = reinterpret_cast<SubClass&>(*this);
            self = m;
        }
        template <typename SubClass>
        void operator = (const SubClass& m) {
            SubClass& self = reinterpret_cast<SubClass&>(*this);
            self = m;
        }
        template <typename SubClass>
        operator SubClass& () { return reinterpret_cast<SubClass&>(*this); }
    };

※これでunionから開放されます

比較的サイズが小さいクラスを実体のままコンテナに格納する場合、継承したクラスオブジェクトを入れる事が難しい。手っ取り早い方法としては、unionを使う事があるが、unionを使うとコンストラクタが書けないなどのC++11以前の制約がある。

例
 
   class Car {
     int cylinders; /* 気筒数*/
     int displacement; /* 排気量 */ 
   };
   class Track : public Car { int payload = 1000; } // 最大積載量
   class Bus : public Car { int maxPeoples = 30; }  // 乗車定員

   vector<Car> carList;
   carList.push_back(Truck()); // NG  TruckがCarでスライシングされてpayloadが失われる
   carList.push_back(Bus()); // NG  BusがCarでスライシングされてmaxPeoplesが失われる

   vector<Car*> carList2;
   carList2.push_back(new Truck()); // OK  Truckの情報も保持される
   carList2.push_back(new Bus()); // OK  maxPeoplesの情報も保持される

ポインタを使えば解決するけど、小さいオブジェクトを全部newで作るのは効率が悪い。

unionを使った解決方法

  class AllCar {
    enum Type { Truck, Bus };
    Type type;
    int cylinders;
    int displacement;
    union {
       int payload;
       int maxPeoples;
    };
  };
  vector<AllCar> allCarList;
  AllCar truck;
  truck.type = AllCar::truck;
  allCarList.push_back(truck);


DynamicPolymorphismを使った例
 
 class Car { 
    enum Type { Truck, Bus };
    Type type;
    int cylinders; /* 気筒数*/ int displacement; /* 排気量 */ };
 };
 
   class Track : public Car { int payload = 1000; } // 最大積載量
   class Bus : public Car { int maxPeoples = 30; }  // 乗車定員
   typedef DynamicPolymorphism<Car, max(sizeof(Truck), sizeof(Bus))>  AllCars;
   
   vector<AllCars> allCars;
   allCars.push_back(Truck());  // AllCarsは継承クラスの最大サイズが確保されているのでスライシングは起こらない
   allCars.push_back(Bus());
 
   Truck& t = allCars.front(); // DynamicPolymorphismのオペレーターでTruckに変換される
   Bus& t = allCars[1];

* operator new の効力範囲 [#tdb48b30]

 #include <stdio.h>
 #include <stdlib.h>
  
 void* operator new (size_t sz) throw() {
 	printf("global operator new\n");
 	return 0;
 } 
  
 int main(int ac, char* av[]) {
 	struct newtest {
		static void* operator new (size_t sz) throw() {
			printf("newtest operator new\n");
			return 0;
		}
		void dotest() {
			char* a = new char;
			printf("a = %p\n", a);	
		}
	};
	newtest t;
	t.dotest();
	newtest* t2 = new newtest;
 };


実行結果

 global operator new
 a = (nil)
 newtest operator new

* 参照型のメンバをもつクラスのコンテナとソート [#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が可能になる

*** コードの例 [#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) {
       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;
 }

* [[C++OPTIONAL]] [#beb6d77b]

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