C++について、知っていそうで知らないこと、素朴な疑問など。(新着順)

定数の定義と静的構造体テーブルを同時に作るマクロ

定数の定義をソースコード上で行う場合、定数の定義と値の定義を同時に行いたい場合があります。

通常はこのように書きます。

 enum EnumHoge { hoge, fuga, piyo };
 struct HogeHoge {
   EnumHoge id;
   float value;
   const char* name;
 } const table[] = { 
  { hoge, 1.0f, "hoge" },
  { fuga, 2.0f, "fuga" },
  { piyo, 3.0f, "piyo" },
 };

enumの定義と構造体の定義が冗長で、名前の文字列の定義も冗長です。
しかし、魂を悪魔(#define)に売り渡せば、一発で記述できます。

 // ここで定数を定義する(冗長でない単一の記載で済ませる)
 #define LIST \
 DEF(hoge, 1.0f), \
 DEF(fuga, 2.0f), \
 DEF(piyo, 3.0f)
 
 // enumの生成
 #undef DEF
 #define DEF(a, b) a
 enum EnumHoge {  LIST };
 
 // 構造体の生成
 #undef DEF
 #define DEF(a, b) {a, b, #a}
 struct HogeHoge {
   EnumHoge id;
   float    value;
   const char* name;
 } const table[] = { LIST };

使用例

 #include <stdio.h>
 int main(int ac, char*av []) {
   for (size_t n = 0; n < sizeof(table)/sizeof(HogeHoge); ++n) {
     printf("table[%d] = id:%d value:%f name='%s'\n", n, table[n].id,   table[n].value, table[n].name);
   }
 }

結果

table[0] = id:0 value:1.000000 name='hoge'
table[1] = id:1 value:2.000000 name='gufa'
table[2] = id:2 value:3.000000 name='piyo'

ideone: http://ideone.com/HEIsAa#view_edit_box

コンテナに対して、pushとpush_backを自動判別して追加するUtility (C++11)

  // decltypeを使ってコンテナに要素を追加する
  // push_back()を持っている場合                                                                                                                                  
  template <typename CT, typename VT>
  inline auto push_to_container(CT& container, VT&& val, int=0) -> decltype(container.push_back(val))
  {
    return container.push_back(val);
  }
  // push()がある場合                                                                                                                                  
  template <typename CT, typename VT>
  inline auto push_to_container(CT& container, VT&& val) -> decltype(container.push(val))
  {
    return container.push(val);
  }

使用例

 std::vector<int> hoge;
 std::priority_queue<int> fuga;
 ... 
 push_to_container(hoge, 1); // hoge.push_back(1)が呼ばれる
 push_to_container(fuga, 1); // fuga.push(1)が呼ばれる

参考にしたページ

メンバー関数の存在を確かめる方法

VC++でイテレーターを高速化する

VC++では、イテレーターの範囲チェックが行われており、安全な反面、若干遅いです。
以下の定義を行うことで、範囲チェックを無効にできます。(C++ポケットリファレンスより引用)

#define _SECURE_SCL 0

ちなみに、以下の設定で範囲チェックが有効になります。

#define _SECURE_SCL_1
#define _HAS_ITERATOR_DEBUGGING 1

boost mpl map for_each覚え書き

boost::mplのvectorやmapをfor_eachするサンプル。 mapのkeyだけ回すmap_keysのようなフィルタの例も作ってみました。

 #include <iostream>
 #include <vector>
 #include <string>
 #include <typeinfo>
 #include <boost/mpl/list.hpp>
 #include <boost/mpl/map.hpp>
 #include <boost/mpl/for_each.hpp>
 #include <boost/mpl/key_type.hpp>
 
 using namespace boost;
 using namespace std;
    
 struct Disp {
     template <class Type>
     void operator()(const Type&) const {
         cout << typeid(Type).name() << endl;
     }
     template <typename K, typename V>
     void operator()(const mpl::pair<K,V>&) const {
         cout << typeid(K).name() << "," << typeid(V).name() << endl;
     }
 } disp;
  
  
 int main()
 {
     typedef mpl::list<int, std::string, char, std::vector<int> > tl;
     typedef mpl::map<mpl::pair<int,char>, mpl::pair<int,int>, mpl::pair<char, char> > t2;
  
     cout << "mpl list test" << endl;
     mpl::for_each<tl>(disp);
     cout << "mpl  map test" << endl;
     mpl::for_each<t2>(disp);
     cout << "mpl  map key_type test" << endl;
     mpl::for_each<t2, mpl::lambda<mpl::key_type<t2, mpl::_1> > >(disp);
     cout << "finish" << endl;
  
     return 0;
 }

cpp_akiraさんのページにあったサンプルにmapを足してみました。

http://ideone.com/rrk1ZY

enumをコンテナのように扱う

http://stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c

http://ideone.com/J6ThVM

普通の配列をboost:arrayに化けさせる

配列を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

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

プログラム中に"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

整数型配列の初期化

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

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の配置構文で確保したときの落とし穴

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

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 で比較してはいけない理由

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

仮想関数の部分オーバーライドについて

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マクロとの戦い

#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)

#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

  • boost poolには、pool, object_pool, singleton_poolの3種類のタイプがある
  • pool
    • malloc/freeを行う
    • コンストラクタでサイズを指定
    • コンストラクタで指定サイズのメモリをアロケート/フリーできる
    • 指定サイズのN倍も可能 (ordered)
    • orderedと通常がある。
  • object_pool
    • クラスのインスタンスを生成/破棄する
    • orderedのみ
  • singleton_pool
  • サイズ別にシングルトンとして動作する。
  • シングルトンとして使える。
  • vectorなどSTLのコンテナのアロケーターとして使える
  • メモリの解放にはpurge_memoryしかない。(全放棄)

orderedとは

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

通常版(非orederd)

  • 破棄したメモリブロックが再利用される
  • release_memoryはできない
  • 全部破棄のpurge_memoryは可
  • 連続したメモリブロックを確保できない

ordered、非orderedともに一長一短で使いにくい

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

#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;
}
 

boost:multiindexは便利

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

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

   // 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 の効力範囲

#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

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

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

 // 参照型のインスタスをもつクラス
 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に格納可能なリファレンスをもつクラスの定義

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

禁断の技その1

Hoge operator = (const Hoge& h) {
   memcpy(this, &h, sizeof(Hoge));
   return *this;
}
  1. thisのデストラクタが呼ばれないで破棄される
  2. オブジェクトのバイナリーコピーは非常に危険なのでダメ

禁断の技その2

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

コードの例

#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の第二構文を使った初期化を使って実現する。
    // 例外安全ではない。デストラクタ・コピーコンストラク

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2013-06-13 (木) 14:20:08 (1804d)