Posts Tagged Programming
C++でのメンバ変数として、動的オブジェクトを作る
C++を勉強していて何が困ったって、メインで動的にメモリ領域をとる方法はたくさん載っているのに、クラスのメンバ変数を動的にとる方法がなかなか載っていない。
と言うわけで、いろいろ試行錯誤した結果、下記のようになった。
テスト用コード
#include <iostream>
#include <string.h>
using namespace std;
class hoge
{
public:
char* szStr;
hoge(char* szNewStr)
{
szStr = new char[strlen(szNewStr)];
strcpy(szStr, szNewStr);
}
~hoge() { delete szNewStr; }
};
int main()
{
char szStr[] = {"ばかやろ~~~~~~~~~~~~~~~"};
cout << szStr << endl;
cout << "end" << endl;
cout << strlen(szStr) << endl;
hoge* obj;
obj = new hoge(szStr);
cout << "\ntest\n";
cout << szStr << endl;
delete obj;
return 0;
}
実行結果。
ばかやろ~~~~~~~~~~~~~~~ end 57 test ばかやろ~~~~~~~~~~~~~~~
まず、hogeなるクラスを定義します、と。
そのメンバ変数に動的に確保したい型のポインタを置きます、と。
それをコンストラクタなり、なんなりでnewしてあげるとそこにで動的に確保される、と。
ちなみに、ここで言う「型」というのは文字通りの型じゃなくても良くて、クラスでも良い。
なぜかというと、C++でのクラスは構造体を拡張したものだから。
その詳細はまた別の記事にて。
そんだけ。
C++でbad_alloc例外を2回取るテスト
と言うか、2回エラーをキャッチすることができるンかい?と言うお話し。
ほら、メモリを取得するときに多めにとってダメで、少なめに取ったらOKかもしれない場合、どうなのかなぁ?とかそんなこと思っただけ。
まぁ、メモリ取れなかった時点で普通はもうやめちゃう訳なんだけれども。
一応出来るのか出来ないのかをはっきりさせたかった。
で、テストコード。
#include <iostream>
#include <iomanip>
#include <new>
using namespace std;
#define MEMSIZE 10000000
int main()
{
double* pDat;
int i = MEMSIZE;
while (true) {
try {
pDat = new double[i];
}
catch (bad_alloc) {
pDat = NULL;
break;
}
cout << "#1 count " << setiosflags( ios::right )
<< setw(2) << i/MEMSIZE << " | "
<< setw(9) << i << "bytes | "
<< setw(6) << i/1000 << "Kbytes | "
<< setw(3) << i/1000000 << "Mbytes\n";
delete pDat;
i += MEMSIZE;
}
cout << "after bad_alloc\n";
i -= MEMSIZE;
cout << i/1000000 << "Mbytes can take " << i/sizeof(char) << " characters\n\n";
i = MEMSIZE;
while (true) {
try {
pDat = new double[i];
}
catch (bad_alloc) {
pDat = NULL;
break;
}
cout << "#2 count " << setiosflags( ios::right )
<< setw(2) << i/MEMSIZE << " | "
<< setw(9) << i << "bytes | "
<< setw(6) << i/1000 << "Kbytes | "
<< setw(3) << i/1000000 << "Mbytes\n";
delete pDat;
i += MEMSIZE;
}
cout << "after bad_alloc\n";
i -= MEMSIZE;
cout << i/1000000 << "Mbytes can take " << i/sizeof(char) << " characters\n\n";
return 0;
}
実行結果。
#1 count 1 | 10000000bytes | 10000Kbytes | 10Mbytes #1 count 2 | 20000000bytes | 20000Kbytes | 20Mbytes #1 count 3 | 30000000bytes | 30000Kbytes | 30Mbytes #1 count 4 | 40000000bytes | 40000Kbytes | 40Mbytes #1 count 5 | 50000000bytes | 50000Kbytes | 50Mbytes #1 count 6 | 60000000bytes | 60000Kbytes | 60Mbytes #1 count 7 | 70000000bytes | 70000Kbytes | 70Mbytes #1 count 8 | 80000000bytes | 80000Kbytes | 80Mbytes #1 count 9 | 90000000bytes | 90000Kbytes | 90Mbytes #1 count 10 | 100000000bytes | 100000Kbytes | 100Mbytes #1 count 11 | 110000000bytes | 110000Kbytes | 110Mbytes #1 count 12 | 120000000bytes | 120000Kbytes | 120Mbytes #1 count 13 | 130000000bytes | 130000Kbytes | 130Mbytes #1 count 14 | 140000000bytes | 140000Kbytes | 140Mbytes #1 count 15 | 150000000bytes | 150000Kbytes | 150Mbytes after bad_alloc #2 count 1 | 10000000bytes | 10000Kbytes | 10Mbytes #2 count 2 | 20000000bytes | 20000Kbytes | 20Mbytes #2 count 3 | 30000000bytes | 30000Kbytes | 30Mbytes #2 count 4 | 40000000bytes | 40000Kbytes | 40Mbytes #2 count 5 | 50000000bytes | 50000Kbytes | 50Mbytes #2 count 6 | 60000000bytes | 60000Kbytes | 60Mbytes #2 count 7 | 70000000bytes | 70000Kbytes | 70Mbytes #2 count 8 | 80000000bytes | 80000Kbytes | 80Mbytes #2 count 9 | 90000000bytes | 90000Kbytes | 90Mbytes #2 count 10 | 100000000bytes | 100000Kbytes | 100Mbytes #2 count 11 | 110000000bytes | 110000Kbytes | 110Mbytes #2 count 12 | 120000000bytes | 120000Kbytes | 120Mbytes #2 count 13 | 130000000bytes | 130000Kbytes | 130Mbytes #2 count 14 | 140000000bytes | 140000Kbytes | 140Mbytes #2 count 15 | 150000000bytes | 150000Kbytes | 150Mbytes after bad_alloc
結論、出来るw
おそまつ。
Visual Studio 2008 Express Editionを使い始める、の巻
ちょっとCとC++の勉強もあったまってきたので、「猫でもできるプログラミング」を参考にしながらWindows SDKプログラミングもちょっくらいじりだそうかと思っている昨今。
さっそくイントロダクションでつまったので、備忘録を載せておくことに。
写経すること数分、正直言ってビルド失敗。
で、チキン野郎なので、そのままコピペ・・・したら(エンコーディングの問題からか、)1行になったので、それを分解・・・。
するとどうでしょう。
うごかねぇ~~。。
そのエラーを探ること数分、見つけましたよ、原因を。
そもそも、このページのオーナーさんである粂井さんが一連の記事を書き始めたときのVCのバージョンが4.0だったことが事の発端なんだとか。
というわけで、どこを直せばいいかを書いておこうかなぁ、と。
まず、コンパイラのエラーを見る。
error C2440: '=' : 'HGDIOBJ' から 'HBRUSH' に変換できません。
'void*' から非 'void' 型への変換には明示的なキャストが必要です。
error C2440: '=' : 'char [25]' から 'LPCWSTR' に変換できません。
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
error C2664: 'CreateWindowExW' : 2 番目の引数を 'char [25]' から 'LPCWSTR' に変換できません。(新しい機能 ; ヘルプを参照)
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
最初に、本来HBRUSH型を要求するhbrBackgroundにGetStockObject(WHITE_BRUSH)を入れていることからエラーとなったご様子。
HGDIOBにキャストしてみる。
2番目はszClassNameがchar型なのに対して、LPCWSTR型を要求する場所を引数として入れていることが問題のご様子。
LPCWSTRにキャストしてみる。
(3箇所)
で、該当箇所はこの辺り。
WNDCLASS myProg;
if (!hPreInst) {
myProg.style = CS_HREDRAW | CS_VREDRAW;
myProg.lpfnWndProc = WndProc;
myProg.cbClsExtra = 0;
myProg.cbWndExtra = 0;
myProg.hInstance = hInstance;
myProg.hIcon = NULL;
myProg.hCursor = LoadCursor(NULL, IDC_ARROW);
// VC 6.0以降は型のチェックが徐々に厳しくなっている。
// 本来HBRUSH型を要求するhbrBackgroundにGetStockObject(WHITE_BRUSH)を入れたところ、
// エラーとなるのでHGDIOBにキャストしている。
//myProg.hbrBackground = GetStockObject(WHITE_BRUSH);
myProg.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
myProg.lpszMenuName = NULL;
// myProg.lpszClassName = szClassNme;
myProg.lpszClassName = (LPCWSTR) szClassNme;
if (!RegisterClass(&myProg)) return FALSE;
}
hWnd = CreateWindow(
//szClassNme,
//"俺にもできる?Windowsプログラミング",
(LPCWSTR) szClassNme,
(LPCWSTR) "俺にもできる?Windowsプログラミング",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
で、これを実行すると・・・。

タイトル、化けちゃってるね・・・。
どうやら、そもそもの修正方法が違うらしい。
粂井さんのサンプルはどうやらSJIS前提らしく、関数もSJIS前提の関数とかっぽい??
現状のプロジェクトファイルはUnicodeで設定されていて、それを直したりすれば行けるっぽい。
とりあえず、文字エンコーディングを直す。
(Alt-F7でプロジェクトのプロパティ)
プロジェクトのプロパティで「構成」が「Unicode文字セットを使用する」とか書いてあると思うんだけど、

「マルチバイト文字セットを使用する」とする。

恐らくこれでSJISになる。
で、今度は別のエラーが。
error C2440: '=' : 'LPCWSTR' から 'LPCSTR' に変換できません。
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
error C2664: 'CreateWindowExA' : 2 番目の引数を 'LPCWSTR' から 'LPCSTR' に変換できません。(新しい機能 ; ヘルプを参照)
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
なんだとか。
・・・LPCWSTR型にしなくてよかった、と。。。
さっきの箇所は、これでいいらしい・・・。
WNDCLASS myProg;
if (!hPreInst) {
myProg.style = CS_HREDRAW | CS_VREDRAW;
myProg.lpfnWndProc = WndProc;
myProg.cbClsExtra = 0;
myProg.cbWndExtra = 0;
myProg.hInstance = hInstance;
myProg.hIcon = NULL;
myProg.hCursor = LoadCursor(NULL, IDC_ARROW);
// VC 6.0以降は型のチェックが徐々に厳しくなっている。
// 本来HBRUSH型を要求するhbrBackgroundにGetStockObject(WHITE_BRUSH)を入れたところ、
// エラーとなるのでHGDIOBにキャストしている。
//myProg.hbrBackground = GetStockObject(WHITE_BRUSH);
myProg.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
myProg.lpszMenuName = NULL;
myProg.lpszClassName = szClassNme;
if (!RegisterClass(&myProg)) return FALSE;
}
hWnd = CreateWindow(
szClassNme,
"俺にもできる?Windowsプログラミング",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
お粗末さまでした。
インライン関数について考える
一個前の投稿ではCの仮引数付きマクロについて取り上げたけど、本題はC++のインライン関数だったり。
仮引数付きマクロでは()で正しく演算順序を指定しないと問題が起きがちですよ、と。
で、CからインクリメントしたC++ではそれを一歩上行くインライン関数と言う物があり、より関数に近い形で置換できると言う優れもの。
使い方は以下の通り。
#include <iostream>
using namespace std;
#define PI 3.141592674
inline double area_of_circle(double r)
{
return r * r * PI;
}
int main()
{
double radius;
cout << "円の半径を入力して下さい。\n";
cin >> radius;
cout << "円の面積は" << area_of_circle(radius - 1.0 + 1.0) << "です。\n";
return 0;
}
実行結果は以下の通り
McLaren% ./inline_test 円の半径を入力して下さい。 4 円の面積は50.2655です。
18行目のように、わざと計算を挟んでみた。
仮引数付きマクロならば
radius - 1.0 + (1.0 * radius) - 1.0 + (1.0 * PI)
となるところを、
(radius - 1.0 + 1.0) * (radius - 1.0 + 1.0) * PI
と言うように、パーレンで囲わなくてもコンパイラが適宜最適化してくれると言う仕組み。
型の指定をしていることからわかる通り、型もチェックしてくれます、と。
これは便利。
ただ、独習C++によると
inline指定子は、コンパイラにとってはコマンドではなく要求であることを覚えておいてください。
とのこと。
どうやらループとか書いてあるとダメなコンパイラもあるらしい。
ただ、普及しているコンパイラはOKっぽい?
そもそも自分で明示的にインライン関数にするのは好ましくないから、コンパイラが自動的にインライン化する機能を有しているので、それに任せた方が良いんだとか。
ん〜、さすが後発言語。
何から何まで便利。
で、インライン化できなかった場合は普通の関数になるらしい。
ここで疑問になるのがプロトタイプ宣言の是非なんだけど、Wikipediaによれば
インライン関数はモジュール単位に定義する必要がある(通常の関数は1つのモジュールで定義すればよい)。これにより、モジュール単位に独立したコンパイルができるようになっている。
となる。
と言うことは、モジュール単位で書くのが当然で、外部から呼び出すのは無理?もしくはナンセンス?
一応ちょっと試したんだけど、ヘッダに書いたら読める。
ただ、別ソースファイルに書いたら読めなかった。
なるほど、独立しているとはそう言うことなのか??
一応動くソースは下記の通り。
(文字列を渡すところで警告あり。でも動く。)
inline_test_main.cpp
#include <iostream>
#include "inline.h"
using namespace std;
int main()
{
double radius;
echo("円の半径を入力して下さい。");
cin >> radius;
cout << "円の面積は" << area_of_circle(radius) << "です。\n";
return 0;
}
inline.h
#include <iostream>
using namespace std;
#define PI 3.141592674
void echo(char []);
inline double area_of_circle(double r)
{
return r * r * PI;
}
inline.cpp
#include "inline.h"
void echo(char str[])
{
cout << str << endl;
}
コンパイルと実行結果は下記のとおり。
McLaren% g++ -o inline inline_test_main.cpp inline.cpp inline_test_main.cpp: In function ‘int main()’: inline_test_main.cpp:9: 警告: deprecated conversion from string constant to ‘char*’ McLaren% ./inline 円の半径を入力して下さい。 3 円の面積は28.2743です。
そんだけ。
Cの仮引数付きマクロ(マクロ関数)について考える
今ちょうどCをまじめに勉強中。
何度目かの正直・・・。
で、仮引数付きマクロ、いわゆるマクロ関数について整理しておこうかなぁ、と。
マクロ関数の基本としては、
#include <stdio.h>
#define PI 3.141592674
#define circle(r) (r) * (r) * PI
#define echo(str) printf("%s", str)
main()
{
double radius; // 半径
char str[] = "半径を入力して下さい\n";
echo(str);
scanf("%lf", &radius);
printf("面積は%.2fです。\n", circle(radius));
return 0;
}
とすると、
McLaren% ./marco 半径を入力して下さい 2.1 面積は13.85です。
となる。
何をやっているかと言えば、定数PIは3.141592674と定義。
問題はcircle(r) (r) * (r) * PI。
ソース内でcircle(radius)と呼び出すとその部分が(radius) * (radius) * PIと置換される。
文字通り置換されるらしい。
そのタイミングはコンパイル時。
で、それを連発すると関数呼び出しによるオーバーヘッドが無い分高速化はされるけど、ソースと実行ファイルは肥大化しますよ、と。
それはカーニハン&リッチーのプログラミング言語C(以下、K&R)に書いてある通り。
(p.109 第2版)
で、なぜ(radius) * (radius) * PI と回りくどい書き方をしているかと言うと、それもK&Rに書いてある。
仮引数r がもしhoge – 100 とかだと、r * r * PI だとhoge – 100 * hoge – 100 * PIとなる。
もうちょっと詳しく書くとhoge – (100 * hoge) – (100 * PI) となってしまう。
だからこう書かないと、期待した値にならないよ、と。
実はこのコードの初版ではそのことに気がつかずに書いていたから、急いで直したのはここだけの話w
そうそう、K&Rでは一言も「マクロ関数」とは書いていない。
あくまで「仮引数付きマクロ」とのこと。
まぁ、実際問題関数じゃないからそこは意見が分かれることなんだろうけど、所詮ニュアンスの違いじゃないの?と思ってしまう今日この頃。
そんだけ。
cygwinでGLUTを使う
とりあえず、setup.exeから探す。
develとか辺りにあるはずなので、それらをチェックしてインスコ。
ちなみに、一番重要と思われるのがfreeglut。
これ入れてないとダメっぽい。
あと、
http://www.xmission.com/~nate/glut.html
からWindows用のOpenGLを入手して解凍する。
その中のREADME-win32.txtに書いてあるとおり、
glut32.dll to %WinDir%\System, glut32.lib to $(MSDevDir)\..\..\VC98\lib, and glut.h to $(MSDevDir)\..\..\VC98\include\GL.
とする。
Cygwinで使う場合はglut32.dllのみを使うので、C:\Windows\system\に設置するのみでOKなはず。
で、実際gccでコンパイルするとき、cigwin上からどうしても実行できなかったので、-mno-cygwin というコンパイルオプションをつけたりする。
ファイル名はソースがglut01.c、実行ファイルはtest_opengl.exe としましょう、と。
gcc -otest_opengl.exe glut01.c -lglut32 -lglu32 -lopengl32 -mno-cygwin
ただ、残念な話、自分の環境ではこれでも動かなかった。
というのも、Cygwinのバージョンが1.7の開発版で、そのGCCがVersion 4でなおかつ-mno-cygwin はベータ版のため、未整備。。
手持ちのGCCは
$ gcc --version gcc (GCC) 4.3.4 20090804 (release) 1 Copyright (C) 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
こんな感じで、GCC3だと-mno-cygwin オプションがいけるらしいので、3を探すことに。
gccと打ってからタブキーを押して
$ gcc gcc-3.exe gcc-4.exe gcc.exe gccbug-3 gccbug-4
と出たので、
$ gcc-3 --version gcc-3 (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125) Copyright (C) 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
こいつでコンパイルしましょう、となると
$ gcc-3 -otest_opengl2.exe glut01.c -lglut -lglu32 -lopengl32 -mno-cygwin
こんな感じ。
ソースは床井さんのページ
http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html
ここの最初のソースを拝借しました。
要するに、ただウィンドウを表示するだけ。
#include <GL/glut.h>
void display(void)
{
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutCreateWindow(argv[0]);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
お粗末さまでした。
MacOSXでのJava文字化け解消とか
移り気な俺はJavaにも興味津々。
と言うわけで、いまさらJavaの端っこをかじることに。
で、Macには標準で入っている。
と言うか、Javaのバージョンアップがあったからちょいとやってみようかと思ったんだけれども。
あと、ちょっとした縁で本を借りたので、内容はWindows用っぽいけどやっぱり無理矢理チャレンジすることにw
で、最初につまずいたのがJava自身の文字化け。
どうやらShift JISをベースに出してくれるらしく、ちょっと面倒なことに。
Javaのバージョンが知りたくて
javac --version
を打ったんだけど、
McLaren% javac --version javac: --version �͖���ȃt���O�ł��B �g����: javac <options> <source files> �g�p�\�ȃI�v�V�����̃��X�g�ɂ��ẮA-help ��g�p���܂�
こんな感じに。
恐らくこのままだとエラーメッセージまでこれw
せめて英語でエラーを受けたいところ。
(文字化けと比較すれば、英語ならまだ読めるw)
で、いろいろ探したところ、シェルの設定ファイルに下記を書き込めば良いとのこと。
恐らく多くの人はbashなので~/.bashrcを。
自分はzshなので~/.zshrcを。
alias javac="javac -J-Dfile.encoding=UTF-8" alias java="java -Dfile.encoding=UTF-8"
で、もう一度Javaコンパイラのバージョンを見てみることに。
McLaren% javac --version javac: --version は無効なフラグです。 使い方: javac <options> <source files> 使用可能なオプションのリストについては、-help を使用します
おお、日本語w
(そして–versionなんてフラグは無いw)
さすが世界企業Sun。
そして愛しのVimの原型viを開発したビル・ジョイも参加して作っただけある。
やっぱすごい。
そして便利そう。
(クソ単純な俺)
そうそう、実は自分のファイルにこれだけは書いてあった。
export JAVA_OPTIONS="-Dfile.encoding=UTF-8"
一度挑戦しようとしかけたのかね?w
とは言え、失敗していたからアレなんだけれども。
と、ここまで書いたんだけど、今CおよびC++の勉強中だから、あくまで気まぐれ。
いつ続きをやるかわかりませんので悪しからず〜。
あ、ちなみに正式なバージョン情報は
McLaren% javac -version javac 1.6.0_17
と言った感じ。
(珍しく、ハイフン1つ少なくていいのね)
そんだけ。
Objective-Cのお勉強 #2 ファイルの分割とコンパイル
Posted by yu++ in Objective-C on 2009 年 10 月 23 日
前回はクラスとメソッドを定義し、それをmain関数から呼び出すと言うことに。
前回までのソースは下記の通り。
// engine.m
#import <Foundation/NSObject.h>
#import <stdio.h>
@interface Engine : NSObject
- (void) startEngine;
@end
@implementation Engine
- (void) startEngine
{
printf( "Start my engine!\n" );
}
@end
int main()
{
// idと言う型の変数を定義します
id obj;
// クラスのインスタンス化
obj = [[Engine alloc] init];
// EngineクラスのstartEngineメソッドを呼び出します
[obj startEngine];
return 0;
}
Objective-Cのお勉強 #1 簡単なクラスとメソッドの説明
Posted by yu++ in Objective-C on 2009 年 10 月 23 日
今回は「クラス」と「メソッド」と言うものについて勉強して行こうじゃないですか、と。
まず、クラスと言うものは、プログラムの部品だと思って下さい。
オイラは自動車が好きなのでそれで例える。
1つのプログラムが自動車そのものだとしたら、クラスとはなでしょ?
答えは簡単で、それはボディーであったり、エンジンであったり、タイヤであったり、サスペンションであったりする。
エンジンも細かく分けて行けばカムシャフトやら、ピストンやら、ブロックやらたくさんある。
それがプログラムで言うところの「メソッド」。
では、実際のコードでエンジンクラスを作っていきましょうかね。
Read the rest of this entry »
Objective-Cのお勉強 #0 準備篇
Posted by yu++ in Objective-C on 2009 年 10 月 23 日
MacやらiPhoneでプログラミングをする場合、CやC++、はたまたJavaなんかを思い浮かべる時代はとうに終わったそうです。。
それを突きつけられたのがこの前バージョンアップしたOS、Mac OS X Snow Leopard。
これに対応したIDE(開発環境)であるXcode 3.2のテンプレートから、C/C++用のCarbonと呼ばれる環境用のテンプレートが無くなり、Javaなんかは結構前から無かったり、PythonやRubyもサポートされていたようなんだけど、それも削除。

マカー系プログラマに残された道はObjective-CベースのCocoa環境しか無いわけですよ。
んじゃ、やっぱ勉強しますか。
CやC++でさえ怪しい身分ですが。。。

