2007年3月10日 (土)

vectorとvalarray

vectorとvalarray、どちらを使うべきか迷ったため、調べてみたところ日本語で読める文献が少ない事に驚きました。

ということで、メモメモっと。

続きを読む "vectorとvalarray"

02:04 午前 C++ | | コメント (4) | トラックバック (0)

2007年1月24日 (水)

stringstreamの衝撃

iostream系は引数が覚えにくいという理由で使ってません。

しかし、気まぐれで使ったstringstreamでちょっとした衝撃を受けました。簡易再現コードは以下の通り。

int i;
double d;

stringstream sstr;

sstr << "10.25"
sstr << "10.50";


sstr >> i;
sstr >> d;

個人的に期待した動作はi=10、d=10.5でしたが・・・

続きを読む "stringstreamの衝撃"

10:07 午後 C++ | | コメント (0) | トラックバック (0)

2006年10月29日 (日)

STLのコンパイルエラーを解読する

STLは便利ですが、煩雑なエラーメッセージに悩まされる事が多いのも事実です。

そんなときにはSTLFiltをお勧めします。

STLFiltは、STLのエラーメッセージを短くします。対応するコンパイラは10種類以上です。

続きを読む "STLのコンパイルエラーを解読する"

03:00 午後 C++ | | コメント (0) | トラックバック (0)

2006年9月13日 (水)

using namespace stdをmainの外に書くな

C++のプロフェッショナルならば、一番外でusing namespaceをすることが、名前空間の汚染にしかならないということは分かっているはずです。

ここはC++言語らしく、使うものは宣言してしまったらどうでしょう?

そこで、using宣言子の出番になります。私が思うに、using namespaceを使うのは手を抜くときだけで十分です。

続きを読む "using namespace stdをmainの外に書くな"

11:51 午後 C++ | | コメント (2) | トラックバック (0)

2006年9月12日 (火)

浮動小数の等値比較は要注意

浮動小数は近似値である。

大抵の人がそれを理解しているはずですが、以下のコードでcos(a) != cos(b)が成立する可能性がある理由を挙げてください。私が確認した限り、VC++ 6, 2003の/Od(最適化無効)で発生、2005は発生しませんでした。

#include <iostream>
#include <cmath>

int main() {

    volatile double a = 1.0;
    volatile double b = 1.0;

    if(cos(a) != cos(b)) {
        std::cout << "cos(a) != cos(b)" << "\n";
    }
    return 0;
}

続きを読む "浮動小数の等値比較は要注意"

12:48 午前 C++ | | コメント (4) | トラックバック (0)

2006年9月 9日 (土)

strcpyは常に危険か?(2)

数ヶ月前にふと思い立ってこんな記事を書いたのですが、思ったより反響が大きいため、多少ブラッシュアップしてみました。目的としては「文字列をコピーする作業を含む関数を作成する時に考慮する事」です。

一番簡単な、関数の引数で長さは与えられない状況を検討します。

引き続き、ご指導いただけたら幸いです。

続きを読む "strcpyは常に危険か?(2)"

08:10 午後 C++ | | コメント (0) | トラックバック (0)

2006年6月27日 (火)

参照(ポインタ)だから関数呼び出しが早い?

「参照(ポインタ)渡しをすると、アドレスが渡されるので早いんだよ」
それを聞いたAさんは以下のような足し算をする関数を書きました。

int add(const int& a, const int& b);

int add(int a, int b)だった時と比べて、どうなったでしょうか?

1.変わらない 2.早くなる 3.遅くなる

コレを間違えるならば、貴方はC言語の初心者です。

# intをCPUが最も処理しやすいと仮定します

続きを読む "参照(ポインタ)だから関数呼び出しが早い?"

12:20 午前 C++ | | コメント (0) | トラックバック (0)

2006年6月 5日 (月)

ポインタ同士の演算結果はintではない

基礎的な事ですが、ポインタ同士の演算結果はptrdiff_t型です。仮にintと勘違いしても、大きな問題になることは稀ですが、このくらいは覚えておくと良いと思います。

定義はC言語だと<stddef.h>、C++言語だと<cstddef>にあり、符号付きの整数型とだけ定められ、その表現バイト数は処理系定義です。

続きを読む "ポインタ同士の演算結果はintではない"

01:27 午前 C++ | | コメント (0) | トラックバック (0)

2006年2月16日 (木)

こんなcase文も有効らしい

最近、Code Completeの第2版を読んでいたわけですが、以下のようなコードも有効らしいです。

さて、最終的なiの出力が幾つになるか分かりますか?

int i = 3, j = 1;
switch(i) {
  case 1:
    if(j == 0) {
      i += 2;
        break;
  case 3:
      i++;
    }
    break;
}

printf("i = %d\n", i);

続きを読む "こんなcase文も有効らしい"

11:49 午後 C++ | | コメント (4) | トラックバック (0)

2006年1月31日 (火)

クラスの配列を作る方法

C++でクラスの配列を作る方法は以下の3つの方法をとるのが普通です。

  • コンテナ(vector)を使う
  • replacement-new(再配置new)を使う
  • クラスをC形式の配列で定義する(※非推奨)

続きを読む "クラスの配列を作る方法"

12:52 午前 C++ | | コメント (0) | トラックバック (0)

2006年1月25日 (水)

namespaceに別名をつける

C++の名前空間は別名を付ける事が出来ます。

例えば以下のように複雑な階層構造をもつクラスがあるとします。

namespace Win32
{
    namespace Network
    {
        namespace Http
        {
            class Connection
            { };
        }
    }
}

Win32::Network::Http内のConnectionクラスを使うのに長々とタイプしたくない場合は以下のように定義します。

続きを読む "namespaceに別名をつける"

10:29 午後 C++ | | コメント (0) | トラックバック (0)

2006年1月16日 (月)

クラス内メンバ変数の初期化順序には規約がある

クラスのメンバ変数は初期化順序が規約により決まっています。

例えば以下のクラスのメンバ変数はどの順番で初期化されるでしょうか?

class T
{
    int a;
    int b;
    int c;

    T() : c(5), a(2), b(4) { };
};

続きを読む "クラス内メンバ変数の初期化順序には規約がある"

09:14 午後 C++ | | コメント (2) | トラックバック (0)

2006年1月10日 (火)

負の数に対する除算は処理系定義になる

標準C++では、負の数に対する除算の結果が保証されません。

手元にある標準C++(X3014:2003)の仕様書にある剰余演算子の項目には以下のように書かれています。

続きを読む "負の数に対する除算は処理系定義になる"

09:23 午後 C++ | | コメント (0) | トラックバック (0)

2006年1月 5日 (木)

コードを高速化する手段

私が知っている事でかなり基本的なことを紹介します。
中級以上向けの話ではないです。

続きを読む "コードを高速化する手段"

02:09 午前 C++ | | コメント (0) | トラックバック (0)

2006年1月 4日 (水)

関数呼び出しの内部を理解する(3)

関数呼び出しを理解する最後のポイントはextern "C"です。

externは通常はリンケージ宣言子ですが、C++の仕様では、"C"などのリテラルがついた場合、他の言語の宣言を変換して利用できるようにする、と書かれています(つまり、リンケージ変換宣言子?)。

結果としてC++からCで作られた関数を呼ぶにはexternが必要になります。また、CからC++で作られた関数は呼べないのです。

続きを読む "関数呼び出しの内部を理解する(3)"

01:11 午前 C++ | | コメント (0) | トラックバック (0)

2006年1月 2日 (月)

関数呼び出しの内部を理解する(2)

今回は、呼び出し規約について説明していきます。

呼び出し規約は関数の引数の引き渡し方、引数利用後の処理の仕方を制御します。
これが関数の装飾名にも影響を与えます。

続きを読む "関数呼び出しの内部を理解する(2)"

03:14 午後 C++ | | コメント (2) | トラックバック (0)

2006年1月 1日 (日)

関数呼び出しの内部を理解する(1)

内部を知らない人はWindowsのDLLで関数が正しく呼べないことが多いです。
また、UNIXを使う方も知識として覚えておいて損はないです。

C++の関数呼び出しについては、コンパイラとリンカが関係しています。

続きを読む "関数呼び出しの内部を理解する(1)"

11:51 午後 C++ | | コメント (0) | トラックバック (0)

2005年12月30日 (金)

memcpyやmemcmpで構造体を処理して良いか?

実装例をよく見ますが、仕様からするとNoです。

そんなの当たり前でしょ、という方はこれ以上読む必要はありません。時と場合によるだろ、と思ってる方は一応読んでみてください。

続きを読む "memcpyやmemcmpで構造体を処理して良いか?"

02:00 午後 C++ | | コメント (0) | トラックバック (0)

2005年12月28日 (水)

strcpyは常に危険か?

当然ですが、Noです。安全に使うことが非常に難しいだけです。

strcpyの定義は

char *strcpy(char *dst, const char *src);

ですが、以下の条件が全て成立しなくてはならない事に問題があるのです。

  • 書き込み先(dst)のサイズを知っている
  • 読み込み元(src)が「'\0'」で終了している
  • 読み込み元文字列の長さが書き込み先のサイズを超えない

続きを読む "strcpyは常に危険か?"

11:49 午後 C++ | | コメント (6) | トラックバック (0)

2005年12月27日 (火)

例外発生箇所に戻る方法はあるか?

標準C++の規約によると不可能です。諦めてください。

C++では例外が起こると、catchを見つけるまで「スタックの巻き戻し」が行われる事が決まっています。

続きを読む "例外発生箇所に戻る方法はあるか?"

11:12 午後 C++ | | コメント (0) | トラックバック (0)

2005年12月23日 (金)

C, C++の標準とは?

良く、Visual C++は標準じゃないからとか、GCCは標準に準拠しているとか聞きます。標準とはいったい何なのかという話です。

C言語とC++は別の課程で標準化が行なわれております。そのため、分けて説明するのが適切です。また、標準化団体にも色々ありますが、国際標準であるISOを基準にして考えるのが普通でしょう。

続きを読む "C, C++の標準とは?"

11:49 午後 C++ | | コメント (0) | トラックバック (0)

悪意を感じるC++コード

初心者の間違いとは違う。

明らかに悪意が感じられるコードをご紹介します。

続きを読む "悪意を感じるC++コード"

10:40 午後 C++ | | コメント (4) | トラックバック (0)

デストラクタをvirtualにする理由

理由もなくvirtualにしていませんか?

ご存知のとおり、virtualと宣言すると、クラスの初期化や破棄にかかる処理が増えます必要がない場合は使用を避けるべきなのです。

続きを読む "デストラクタをvirtualにする理由"

01:00 午前 C++ | | コメント (2) | トラックバック (0)

2005年12月19日 (月)

無料のC++コンパイラを活用する

Microsoftの「Visual C++ 2005 Express Edition」が1年間限定で無料配布中です。

もちろん制約があるわけですが、私が厳しいと思う制約は以下の通り。

続きを読む "無料のC++コンパイラを活用する"

11:35 午後 C++ | | コメント (0) | トラックバック (0)

2005年12月 7日 (水)

シフト演算子

C言語においてビットシフト演算子(<<や>>)は2通りに解釈される。

  • 算術シフト --- 符号を意識したシフト
  • 論理シフト --- ビット列としてのシフト

どちらが適用されるかは、型が符号を持つかに依存する。

signedである場合は算術シフト、unsignedの場合は論理シフトである。しかし、符号付整数の右シフトだけは結果が処理系に依存(※1)してしまう。浮動小数型の場合、シフト演算自体が無効である。

signedである場合は算術シフト、unsignedの場合は論理シフトになり、符号ビットを持つfloatやdoubleは算術シフトになる結局は何の違和感もないように出来ているだけだ。

---

※1 Cでは、整数の内部表現を2の補数に決めていないため、下手に規定しないほうが高速なように実装を選べる余地が残せるという事なのだろうか?もっと詳しく知りたいため参考情報募集してます。

---

[MSDN] - C++ Language Reference Shift Operators: >> and <<

---

職業としてのプログラミング - ビットシフトの落とし穴

にて詳しい解説を作られたようです。こちらも是非参考に。

---

(2006/01/05) コメントによる指摘により訂正。progerさん有難うございました。

(2006/01/09) 不適切な表現を修正。参考となるリンクを追加しました。

01:35 午前 C++ | | コメント (8) | トラックバック (1)

2005年11月17日 (木)

switch文の途中で宣言する

普通に書くとswitch文の途中で変数を宣言することが出来ません。

続きを読む "switch文の途中で宣言する"

08:29 午後 C++ | | コメント (0) | トラックバック (0)

2005年11月13日 (日)

operator new

operator newで生成したクラスはコンストラクタを呼ばない。

他の用途としてはクラスの配列(※1)を動的に使いたい時に便利。

T* pClsArray = new T[size]; // デフォルトコンストラクタで初期化

初期化されては困る場合に以下のようにする。

T* pClsArray = static_cast<T*>(operator new [](sizeof(T) * size)); // メモリ確保のみ

・・・

new (&pClsArray[n]) T(); // 配置newで必要な時に作成

デフォルトコンストラクタ以外もちゃんと指定できる。

メモリの解放はoperator deleteを使う。

operator delete [] (pClsArray); // メモリの解放

配置newにdeleteは要らないが、メモリの解放前にデストラクタを手動で呼び出す。

pClsArray[n]::~T(); // これが忘れがち

もちろん配列の個数分必要。

※1 vectorを使う方法も有ります。

---

(05/12/05) 加筆&文法ミスを修正。

12:33 午後 C++ | | コメント (0) | トラックバック (0)

2005年11月12日 (土)

コピーコンストラクタの実装

が未だに悩みます。

クラスがポインタ型の変数を持つ場合です。

この際はデフォルトのコピーコンストラクタが使い物にならなので自分で書かなくてはなりません。中身の全てをヒープ(フリーストア)にとっておくのが唯一の正解と思っています参照カウンタを使っても呼び出し元の不正を完全に防ぐのは難しいし。それでも悩むのはコピーのコストを気にしすぎなんでしょう。

結局は、「呼び出し元が賢いと仮定する」か、「解放の責任を負わせない(≒余計なメモリを消費して安全)」かの2択になります(auto_ptrやshared_ptr(※1)は呼び出し元が賢いと扱う)

答えの違い(※2)を楽しめるのが「C++(※3)」って事で。

※1 C++のデファクトスタンダート?なライブラリ「boost C++」内にある参照カウンタ付きのポインタ。これが常に使えれば個人的には解決なのですが・・・。

※2 毎回違うのが正解という考え方もあります。オブジェクトが小さければコピーを取っても良いし、大きければコピーを生成するのは悪です(一瞬とはいえ、2倍以上メモリを使用します)。最近はプロトでさえ常に安全サイドに傾かせています。

※3 「Managed C++」や「Java」はメモリ管理で楽しめないため嫌いです。

03:03 午後 C++ | | コメント (0) | トラックバック (0)

2005年11月 4日 (金)

定数の末尾

最近、定数の後にLをつけろいったら何ソレといわれました。

C++の規約では整数はint、少数はdoubleになります。末尾に特別なアルファベットをつけるとそれを変更することが出来ます。

符号無し 0U

LONG型  0L

FLOAT型 1.0F

他もあると思いますが、一般的にはこのくらいかと。

---

(05/12/08) doubleについて抜けていたのを追記。

04:34 午後 C++ | | コメント (0) | トラックバック (0)

適切なcast

「キャストをしてみたらコンパイルが通った」などという話を聞きます。

私の前でこんな事を言うと、駄目プログラマのレッテルがアロンアルファでくっつきます。キャストに関わるTipsを少し。

■ 絶対にC++形式のキャストを使う

具体的にはstatic_cast, dynamic_cast, const_cast, reinterpret_castです。C++形式のキャストはC形式のキャストに比べて非常に安全です。当然、人のソースと接続するために、どうしてもキャストしなければならない場合だけ利用します。

詳しくはMSDNの記事、「Deep C++」の「static_cast」あたりから読むと良いと思います。

私のオススメはconst_castです。コイツはconstを付けたり消したりする事しか出来ない。Cのキャストに慣れた方は、制約されたキャストを使用する事から始めましょう。

■ C++のoperatorを理解する

C++の組込型(int, double等)以外、型が勝手に変換される(※1)なんて事はありません。例えばStringというクラスに対して「operator const char*() { return 中身; }」などと定義する事によって、利用者が意識せずともStringがchar*型に変換されているだけです。つまり、意識しないとoperatorは凄く危険(※2)です。

CStringなどをなんとなく利用して、なんとなく変換してもらってるという状況はやめましょう。型が変わっている時は再確認(※3)する。基本です。

とりあえずこの2点ですね。気づいたらまた書くかもしれません。

C++の設計者は、「C++のキャストは置換しやすく、「敢えて」打ちにくくした。本来キャストなど不要だ。」という趣旨の話をしております。

※1 暗黙の型変換はコンストラクタのみに存在します。int型なのにdouble型として初期化したりしますが、当然i余計にスタックを確保している事をお忘れ無く。一時オブジェクトと呼ばれちょっとした嫌われ者です。(興味ある人は暗黙の型変換を禁止する「explicit」も調べてみてください)

(05/11/10追記)

operatorを暗黙の型変換と呼ぶ人もいます。私は、(警告は出るが)型が違ってもコンパイルを成立させてしまうものを暗黙の型変換と呼ぶことにしてます。

※2 C++の標準ライブラリであるbasic_stringがchar*に対するoperatorを使わずに「c_str」なんて関数を定義したか、良く考えましょう。分からないうちは自分で定義してはいけません。

※3 組込型以外は必ずincludeしているヘッダにtypedef式があります。型がoperator式を持つクラスな場合、ヘッダには宣言部分が存在します。

03:57 午後 C++ | | コメント (0) | トラックバック (0)

2005年11月 1日 (火)

適切なconstの使い方(2)

最近出た本である「C++ Coding Standards」を買いました。

著者は「C++ Depthシリーズ」の過去の著者二人です。素晴らしく良くできているとおもいますので是非読んでみてください。

constについての見解も再掲載されていました。

■値渡しの場合、関数宣言(※1)にconstは不要

同氏が前書いたことと若干違います。

違う点は

int Func(int nVal);

宣言し、実際は

int Func(const int nVal)

{ ・・・ }

定義する例を追加した点。

コレ、実は私もやっていました。確かに変数を弄る危険はなくなりますが・・・。

定義の一部にconstを書き忘れて、統一性が無くなる。(経験則)

コンパイラがこの2つを同等に解釈する(事が多い?)のを知らない人も居る為、混乱を招く。(特にクラスのメンバ関数)

そうなる位なら宣言にもconstを付けて定義はコピペした方が良いと思うんですが。Grepも置換もしやすいですし。

※1 宣言と定義の区別がついていない人、結構います。ヘッダファイルに書くべき事が宣言、ソースファイルに書くべきなのが定義です。不安だったら調べなおしましょう。(最近はヘッダに実装を書かざるを得ない場合もあります。VC++もexportのサポートを切望。)

08:37 午後 C++ | | コメント (0) | トラックバック (0)

2005年10月29日 (土)

適切なconstの使い方(1)

関数の仮引数編。元ネタはExceptional C++。

■値渡しには不要。

■ポインタ渡し、参照渡しには必要。

void Func(int nVal);【値渡し】

void Func(const char *szStr);【ポインタ渡し】

void Func(const T& cls);【参照渡し】

特に異議無い。しかし、私の中では例外あり。

■Win32プログラミングでバイト列を読み込む

void Func (const BYTE* pbDoc, const DWORD cbDoc);

constを対照にする為だけに利用。

Win32APIでは「const BYTE*」を「LPBYTE」に「typedef」して利用、自然に見える。

だが、この手の関数は、BYTE*で受けて参照することも多い。関数内でconst BYTE*による参照を強制したいし。

ちなみに、文字列には「LPCSTR」が存在するので問題なし。

11:44 午後 C++ | | コメント (0) | トラックバック (0)