SyntaxHighlighter

2011年11月12日土曜日

C++11 に合った書き方 その2

C++11 に合った書き方 その2

昨日の続きです。C++11 らしい書き方のページについて、気になるものを中心に書いていきます。

C++11 らしい書き方とは

スマートポインタを使え

スマートポインタを積極的に使え、ということのようです。

これまでにも、auto_ptrがありました

しかし、このauto_ptrは非推奨となり、代わりにunique_ptrを使えとのことらしいです。

私は、関数内部でnewしたインスタンスをauto_ptrにくるんで返り値として返す、ぐらいしかauto_ptrを使っていませんでしたが、そういった場合は、auto_ptrのムーブセマンティック考慮版とも言えるunique_ptrの出番のようです。

その他には、既に多くの処理系で実装されているshared_ptrなどが標準として入るようになりました。

これまでにも使っていましたが、仕事で使う際に、「仕様に入っているから」と堂々と使えるようになるのはありがたいことです。

nullptr

これは、単純に0でもNULLでもなく、新予約語のnullptrを使いましょう、ということです。

C++ では、C言語と異なり、void *を任意の型のポインタに代入することができません。例えば、以下のようにNULLを定義していると、C++ ではコンパイルエラーとなります。

1
2
#define NULL ((void *)0)
char *ptr = NULL;  // コンパイルエラー (C言語では OK)

そのため、基本的にNULLは、単なる 0 と定義されています。

C++ では 0 は特殊な扱いをうけており、何も指さないポインタの値として、任意の型のポインタに代入することができると定められています。

一方で、0 は整数値なので、NULLを 0 と定義した場合、以下のようなオーバーロードされた関数がある場合に、多くのプログラマの直感と異なる挙動を示してしまいます。

1
2
3
4
5
6
7
8
void func(int num);   // (1) 引数が int の関数
void func(char *ptr); // (2) 引数が char* のオーバーロードされた関数
 
#define NULL 0
void test()
{
    func(NULL);  // NULL は 0 なので、(2) ではなく (1) が呼ばれる。
}

このように、直感と異なり (1) が呼ばれてしまいます。

C++11 では、このような事態を避けるために、nullptrという新たな予約語を定義し、何も指さないポインタの値として使え、かつ整数として扱われないものが導入されました。

Uniform Initialization and Initializer Lists

ここに例として挙がっているものと同様な下記コードで、私も以前にとても困ったことを覚えています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A
{
};
 
class B
{
  public:
    B(const A &a);
};
 
void func()
{
    B b(A());  // (1)「『返り値の型が A である関数ポインタ』を引数にとり、
               //    返り値の型が B である関数 b」の宣言
    B b((A()));  // (2) B 型の変数 b のコンストラクタに A の一時オブジェクトを与えている
    B b = { A() }; // (3) C++11 で可能になる書き方
}

最初は (1) のようなコードを書いており、なぜコンパイルエラーになるのかがまったく分からず、ふとしたはずみで (2) のように書くとコンパイルが通ったことを覚えています。

C++11 では、{, }による初期化が可能になるので、これらを使うことで、上の例のような場合を避けることができるようです。

また時間があれば、しっかりと C++11 の勉強もしようと思います。

0 件のコメント:

コメントを投稿