アコニック・ランド:プログラミング(主にVC++)メモ

プログラミング(主にVC++)メモ

DirectX7からDirectX9への移行メモ

コンストラクタ呼出前に参照される例
負の数の立方根
atan2と0
JOINについてのあれこれ
プログラミング言語による剰余(余り)や累乗の表現
constポインタの参照エラー
VC++6.0→2013の引継ぎ
IMEの無効化
基底クラスオブジェクトから派生クラスオブジェクトへの代入
コンストラクタ・デストラクタの手動呼び出し
切り詰め代入の警告漏れ
未初期化変数参照の警告漏れ
関数の皮をかぶったマクロに注意
virtualの欠点
new[0]の非推奨
配列引数における注意
条件演算子を用いた参照に関する注意


コンストラクタ呼出前に参照される例

C++において、オブジェクトを宣言するとコンストラクタが実行されるが、
グローバルで宣言したオブジェクトのコンストラクタの中では、
他の未だコンストラクタが呼び出されていないオブジェクトを参照する事ができる。
複数のグローバルオブジェクトを宣言する場合には注意が要る。

2024.2.4


負の数の立方根

-1の立方根(の内の実数解)は通常、-1となるが、
C++の場合、pow(-1,1.0/3.0)で立方根を求めようとしても、
負の数の平方根を求めようとした時と同様、結果の値が「-1.#ind」となってしまう。

2023.10.8


atan2と0

Cでatan2を使うにあたって、
atan2(0.0,-1.0)とatan2(-0.0,-1.0)の結果に違いがある事に注意が要る。
前者はπを、後者は-πを返す。
一方、0.0==-0.0は真と判定される。

2023.9.30


JOINについてのあれこれ

SQL言語のJOINには、
INNER JOIN、LEFT (OUTER) JOIN、RIGHT (OUTER) JOIN、FULL (OUTER) JOIN、CROSS JOINがある。
LEFT JOIN、RIGHT JOIN、FULL JOINは、まとめてOUTER JOINと呼ばれる。

ここで、INNER JOINとOUTER JOINは対義のような名前になっているが、
かなり性質の違うものだと思う。

INNER JOINは、結合先で条件に合うレコードの組がn個あれば、
シンプルにn個のレコードが結果に追加される。0個ならば何も追加されない。
対してLEFT JOINは、n>0ならばn個、n=0ならば片方の分が空白となっている
1個のレコードが追加されるという、少々複雑な処理を行っている。

また、対義的に見るならば、INNER JOINとFULL JOINを対立関係に置いた方が解り易い。
その間にLEFT JOINとRIGHT JOINが来る。

本来、OUTER JOINと呼ぶべきものは、条件に合ったレコードと結合したものに、
片方の分が空白のレコードと結合したものを加えたものであると思う。

CROSS JOINは結局の所、INNER JOINを無条件で行うものと等しく、
INNER JOINの一種という扱いができる。
OUTER JOINと異なり、いずれか片方のテーブルが空の場合は、
空の結果となる点に注意が要る。

対して、無条件のFULL JOINは、
レコードが0〜1の2つのテーブルを単にくっつけたい場合に使える。
CROSS JOINよりも使い道があるのではないかと思うが、
CROSS JOINのような特殊な呼び方が見当たらないのが不可解。

2023.6.3


PostgreSQLで重複行削除

PostgreSQLで、全く同じ内容のレコードを作ってしまった場合に、
これを片方だけ削除する方法に困っていた。
データベース管理システムによっては行ポインタというものが存在するそうで、
利用できそうだが、PostgreSQLの場合はそれも見当たらない。
PostgreSQLの場合はoidというものがそれに相当しているらしいが、無いと言われる。

そんな中、ctidを利用する事で解決することがわかった。
ctidはユニークな隠しレコードみたいなもので、
SELECT ctid,* FROM (テーブル名);
の形で、各レコードデータとの対応表が得られる。
後はWHERE句で
ctid='(0,13)'
のように指定する事で、レコードを特定できる。

ctidは、update毎にカウントされるが、
vacuum full (テーブル名);
とする事で振り直される。

2022.3.24


related_nameとsetとannotate

Djangoでモデルの逆参照のための名称を手動で指定する際、related_nameを用いるが、
これを利用するには注意点がある。
例えば、逆参照先が多であり、名前がTestModelの場合、
testmodel_setという形で逆参照ができるが、
annotateでの指定の中では単に"testmodel"となる。

これに対し、例えば
related_name="test_model_set"
とすると、
test_model_setという形で逆参照ができるが、
annotateでの指定の中でも"test_model_set"と、
_setを付けねばならなくなる。
逆に
related_name="test_model"
としたならば、annotateでの指定の中でも"test_model"で良くなるが、
逆参照の際にも単にtest_modelでなくてはならなくなる。

つまり、本来違う名前だったものが、related_nameを使うだけで、
同一の名前に強制的になってしまう。
これでは不都合な事も多いのではないかと思う。

対処としては、
related_name="test_model_set"
に加えて、
related_query_name="test_model"
を指定すれば、test_model_setという形で逆参照が可能なまま、
annotateでの指定の中では単に"test_model"でよくなる。

ついでに、データベース上のテーブル名も変える場合は、
以上に加えて、Metaの所に
db_table="テーブル名"
という形で指定する。
ただ、通常テーブル名は「アプリ名_モデル名の小文字」
となっている、例えばアプリ名がtest_appならば、
今回の例ではtest_app_testmodelという名前になる所が、 db_table="test_model"
とすると、単に「test_model」という名前となってしまう。

他の自動的に命名されたテーブルと命名規則を合わせるならば、
db_table="test_app_test_model"
とせねばならないが、後からアプリ名変えたいなんて時には、
該当する箇所を全て書き換える必要が出て、少々スマートでないと思う。
%(app_label)sを利用した方法では上手く行かなかった。
makemigrationの段階では、ちゃんとアプリ名に変換してくれないようだ。
ひとまず、appsで定義されているアプリ名を利用する形ならば上手く動作したが、
これで良いのかはわからない。

2021.5.23


PythonはJavaScriptと違って多重継承できるようだが、
基底クラスのコンストラクタは最初に継承したものしか実行されないっぽい。
コンストラクタもオーバーライド扱いになってしまうというわけかな。
折角多重継承ができても、これでは有難味が薄いかな…。
C++の場合、コンストラクタはクラス名と同じ名前だから、
必然的にオーバーライドにはならないし、自動的に実行されるが。
C++よりも融通が利くはずのスクリプト言語にこんな落とし穴があるとは。
特別な事情が無い限りは、メンバオブジェクト……いや、プロパティっていうのか、
として、基底にしようとしたクラスのインスタンスを作った方が良さげかな。

あと、クラス一般の話だが、メソッドの引数にいちいちselfを取る必要があるのが、
JavaScriptやC++に比べて面倒くちゃい所だと思う。
クラスメソッドとの兼ね合いもあるのかもしれんが。

2021.4.11


Pythonは循環importってのが厄介だ(Pythonに限らないかもしれないが)。
互いに一部の関数だけをimportしたいだけで引っかかってしまうのが辛い。
Cの場合なら、宣言をヘッダに記述したり、プリプロセッサでのインポート済み判定を
利用して、回避ができるが、Pythonでimport済み判定を行おうとしたら、
import済みのはずなのにimportしたはずのものが定義されてないという事態に。

ただ、ローカルスコープ内でimportした場合、循環import扱いとならない場合もあるようで、
これによってある程度は回避できそうだ。

2021.4.11


Pythonでは複数行をまとめてコメントアウトする場合に、
三重のダブルまたはシングルコーテーションが使われてるが、
これらは文字列という扱いでもあるから、
リストの定義の中などで用いるとエラーや不具合が起こってしまうのが難点。
普通の複数行コメントアウトも有ると有難いんだけどなぁ……。
一応、vscodeの場合ならば、「Ctrl+K」→「Ctrl+C」で単行のコメントアウトを複数行まとめて行う事が、
そして「Ctrl+K」→「Ctrl+U」でコメントアウト解除ができるようだ。

2021.4.11


Pythonを勉強しているが、配列をリストとタプルに分ける意義がよくわからない。
タプルはリストの定数版のように解釈してるが、
[6, 8] == (6, 8)が偽になるのは地味に不便ではないか。
そして、集合(set)には定数版たるfrozensetが用意されており、
こちらは{6, 8} == frozenset({6, 8})が真となる。
また、バージョンにもよるかもしれないが、辞書(dict)にはなぜ定数版が無いのか。

集合の要素にはタプルや凍結集合は指定できても、変更が可能であるリストや通常の集合は指定できないが、
タプルの要素としてはリストも集合も指定でき、各要素に対する変更も反映されるというのも謎。

2021.3.27


プログラミング言語による剰余(余り)や累乗の表現

剰余累乗整数除算
(整数型同士)
非等値コメント
ExcelMOD(A; B)A ^ BROUNDDOWN(A / B)A <> B
CA % Bpow(A, B)A / BA != B//
BASICA mod BA ^ BA \ BA <> B'
JavaA % BMath.pow(A, B)A / BA != B//
JavaScriptMath.trunc(A / B)
PerlA % BA ** Bint(A / B)A != B#
RubyA / B
PythonA // B

2021.3.17


DirectXの最新のSDKでは、DirectMusic関連をコンパイルできず、なおかつ古いSDKが配布されてないのが非常に困る。
下位互換は保たれるという話じゃなかったのか…。
コンパイルができなくなるって結構不便な事だし、その辺は互換を保って欲しかった。

それにMIDIって軽くてバックアップも取り易いのが依然かなり強みだと思うし、
音色やピッチのみを変えるという事も原理的にはできそうだし、見直されないものか…。
フリゲでも、最近のものは音声ファイルのサイズが膨大なものが多く、
バックアップを取る事が困難になってるのがわりと難題になってると思うんだけどなぁ。

2020.3.7


バックアップを取るためにsdfやipchが邪魔で、なんとか出力位置だけでも変えられないものかと思ってググってみたら、
幾つかのサイトで方法が紹介されていた。
https://shibamu.hatenadiary.org/entry/20101014/p1

その通りに、「オプション」→「テキスト エディター」→「C/C++」→「詳細」の所で、
「常にフォールバック位置を使用」をTrueにし、「フォールバック位置」に絶対パスを指定としたら上手く行った。
相対パスを指定しようとしても上手くいかなかった。

2020.2.19


昔作ったエディタが起動した瞬間に正常に表示されない問題が発生したが、
どうも昔はWM_SIZEがWM_PAINTの先に出てたのが、後に出るようになってたみたいだ。

2020.2.19


Win8.1の環境では、GetSaveFileName等で初期ディレクトリを指定できるはずの
lpstrDefExtが、なぜか反映されなくなってるようだ。
カレントディレクトリとは別に、Windows側でソフト毎に記憶されてるものが、
GetSaveFileName等の初期ディレクトリとなってるようだ。
lpstrFileにパス付きファイル名を指定すれば、そのフォルダが初期フォルダになるのだが、
パス名だけではGetSaveFileName等が失敗してしまって苦しい。

2019.10.8


Direct3Dではディフューズ色やアンビエント色の計算はテクスチャ合成前に行われて、
スペキュラ色の計算だけ後に行われてるようだ。
テクスチャ有りで陰影を付けたい場合は、白地に不透明なものを乗算にするしかないのかな。
この辺の処理の順番なんて簡単に変えられそうなのに、案外ググっても話題にすらなってない。
この辺はもう古い機能なのかな…。

2019.5.16


HALFTONEモードでのStretchBlt、どうも単純に中間色を取ってるわけでは無いようだ。
暗い緑と黒しか無い画像を縮めた際、どう平均を取っても出ないはずの明るい緑が、
色の境界辺りに出て来る。
逆に、白と他の色との境界が、他の色より暗くなる場合もある。
境界がはっきるするためか、単純に中間色を取る場合と比べて綺麗になる場合もあるが、
若干汚い縮小になってしまう場合がある。
両方のモードが用意されていれば便利なのに…。

2019.5.13


constポインタの参照エラー

ポインタの参照型を扱う際、謎の厄介な問題がある。

func(const char *(&str));

と定義した関数に対し、

char *str;
func(str);

とするとコンパイルエラーになってしまう。

func(char *(&str));

という宣言ならば通るが、今度は当然ながら、

const char *str;
func(str);

で上手く行かない。
対策は無いものか探していた所、七空白のブログさんのこのページで同様の指摘と、それに対する対策が記されてるのを見つける。
今回の例の場合は、

func(*(const char **)&str);

とする事で上手く行った(面倒臭いが…)。

これらは、もう少し本質的な問題としては、
const char *str;
char **str2 = &str;

char *str;
const char **str2 = &str;
が各々、「'const char **' から 'char **' に変換できません」
「'char **' から 'const char **' に変換できません」
というエラーを出す事が挙げられると思われる。

これらは、各々以下のようにキャストすると通る。
const char *str;
char **str2 = (char **)&str;

char *str;
const char **str2 = (const char **)&str;

2018.12.8-2022.10.4


CreateFontは
CreateFont(16, 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, L"System");
と設定すると、何もしてない時と同じ文字フォントになるようだ。
feceにNULLを指定した場合はMS UI Gothicと解釈されるようだ。
また、Systemでは中途半端な拡大ができない。

2018.9.12


プログラムのビルド時に「コード生成しています」で何分も待たされる事に悩まされていた。
SDLチェックをOFFにしてみたが全く効果無かった。
超大になってた関数を幾つかに分けた事で普通な速度に戻った。
SDLチェックをONに戻しても問題無かった。

2018.3.18


ワールド座標変換に伸縮(D3DXMatrixScaling)を加えた場合、
ライトの影響、特にスペキュラの影響が大きく変化してしまう。
どうも座標変換の際に法線の大きさが変わってしまってるようで、
それが原因の様子。
SetRenderStateでD3DRS_NORMALIZENORMALSをTRUEにすれば一応解決する。

ここで気になったのは、立体を縮める操作を法線にも適用したとしたら、
法線ベクトルの絶対値も小さくなり、すると光は暗くなるはずなのに、
逆に明るくなっている点。
どうも、小さくしない代わりにわざわざ大きくしているようなのだ。
D3DRS_NORMALIZENORMALSで負荷を掛け損な気がしてしまう。

これは、物体を伸縮する場合のための変換のようだ。
物体を全体的に大きくする場合には法線をいじる必要は無いが、
縦だけに引き伸ばすなどの場合はそうは行かない。
後は内部的にどう計算してるかかな。

2016.5.7


射影行列は、D3DXMatrixPerspectiveFovLHが解り易い。
でも、正面から見た状態を同じ画面内の違う位置に貼りたい場合は、
D3DXMatrixPerspectiveOffCenterLHが便利。
(もっと良い方法有りそうな気もするけど…)
b - t = 2 * tan(fovY / 2);
r - l = (b - t) * Aspect;
で変換で来たと思う。
後はrとl、bとtの値に差を設けてやる事で、表示位置だけをずらす事ができる。

2016.5.5


スペキュラ光は…
SetRenderStateでD3DRS_SPECULARENABLEをTRUEにしないと発動しない!
頂点情報にスペキュラ色を加える必要は無い。

2016.5.5


なんとなくDirextX9Graphicsを触ってみようと思ったら、極めて基本的な所で悪戦苦闘した。
書いてある通りにやってるのに上手く行かない。

まず、2次元の例までは表示されるのに3次元が出ない。
座標の中心が画面の中心に移ってるため、画面外に近い位置になってしまってた上に、
裏と表が逆転していた。
XYZRHW仕様からXYZ仕様に移行し、3種の行列をちゃんと指定する事も重要。

次に、ライトがつかない。
これについては、(マテリアルの設定に加え)頂点要素に法線ベクトルを含め設定する事で解決した。

更にZバッファが全く機能しない。
これについては、D3DXMatrixPerspectiveFovLHの近クリップ面に0.0fを指定してたのを1.0fに
変えたら上手く行った。

ついでにBackBufferのデバイスコンテキストが取得できない。
D3DPRESENT_PARAMETERSのFlagsにD3DPRESENTFLAG_LOCKABLE_BACKBUFFERを指定したら正常に機能した。

2016.4.20-21


なんとなくDirextX9を触ってみようと思い、SDKインストールしてみた所エラーがでた。
http://nanoappli.com/blog/archives/4739
の通りにやったら上手く行った(最後の処理はスタート画面→PC設定→保守と管理→Windows Updateの所で自動的に出てた)。
「d3dxof.libが開けません!」言われたので調べてみたが、
言われた通りに設定しても上手く行かない…と悩んでいた所、
どうも昔のSDKとは異なり、Lib/ではなくLib/x86/を指定しなければいけなかったようだ。
「追加の〜」の方を指定しなくても、VC++ディレクトリの方を指定しただけで動いた。

2015.7.17


VC++6.0→2013の引継ぎ

勝手にWindows8.1にされてしまったために6.0が動かなくなってしまったが、
過去のソフトを久々に改造したくなり、観念してExpress(2013 for Windows Desktop)導入した。
デフォルトでは、これをインストールしてない環境での起動にはランタイムが要る設定になってるみたいで、
ランタイム要らないようにするには、(Rleaseにした上で)プロジェクトを右クリックし、
プロパティ→構成プロパティ→C/C++→コード生成の所で、ランタイムライブラリを
マルチスレッド(/MT)にしておく必要がある。
親父が先に導入してなかったら気付かなかった。危ない。
尤も、引き継ぎプロジェクトの場合は、最初からこの設定になってた。
作業用ディレクトリは設定し直す必要があった。

また、DirextXのディレクトリ指定法が変わり、プロジェクト毎になってた
(構成プロパティ→VC++ディレクトリの所の、インクルードディレクトリで指定)。
DirextX8SDKの場合、指定しただけではエラーが出たが、〜 陽射し 〜さんに従い、
インクルードディレクトリの所で、
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include
(バージョンの違いからか、パスが少し異なってた)
をDirextXの物の前に指定したら、警告は出たものの解決した。

また、サウンドロード用に本付属サンプルのコードを使っていたが、そこのfstream.hがインクルードできなくなってた。
.hを取ればインクルードできたが、end⇒ios::endのような書き換えが必要のようで、しかもいまいちうまく動作しなかった。
後のソフトではfstreamを使わない形式に書き直してたが、そちらを使ったら正常に動作した。
DX8のDirextAudioを最初試してみたんだが、特定のMIDIファイルを鳴らそうとしたり、
セカンダリで効果音を鳴らそうとすると、特定の効果音を超連打しようとすると落ちる問題があった。
前者は、MIDIファイルを最新版に置き換える事で、後者は、連打を控える安全装置を付ける事で解消されたが、
先の方法で解決する事が判ったのでDX7仕様に戻した。

6.0からの引き継ぎは、.dswを開こうとしたら自動的にやってくれた。
ただ、その後からは.vcxprojあるいは.slnを開くようにしないといけない。
また.dswを開こうとしてOKとかやると、折角設定し直したプロジェクトの設定を再度やり直さなければ
いけない(何度もやり直してしまった…)。

2015.1.12


ひいいエクスプレスさん
}else{
を勝手に
}
else{
に書き直すのやめてくだしあ。

2015.1.7

ツール→オプション→テキストエディター→C/C++→書式設定→改行で、「新しい行に'else'を設置」をOFFで解決しました。

2015.2.19


long doubleって型がちゃんとあるにも関わらず、double型と全く同じなのが実に残念。
最近のエクスプレスでも未だ同じらしい。
四倍精度化しているか、少なくとも拡張倍精度化していれば、移行する価値もあると思うんだけどねぇ。
double型じゃint64型との相性がな…。1.0を掛けただけで値が変わってしまう事があるよ。
RPGの経験値の値とか、ちょっと変な事をしようとするとすぐlongの限界を超えてしまうからね。
自前での精度対策は面倒だし心許無い。

2014.10.22


newが失敗している、なぜだ、と思って散々悩んでたら、直前のif構文(消し忘れ)の後の;を忘れていたせいで、
確保の処理そのものがされてないだけだった。

2014.10.9


同じ生の文字列("abc"等)を複数回使用する際、自分の使っているVC++6.0では、
文字列毎にアドレスが異なっているが、最近の物では、同じアドレスになっている模様。
つまり、自分の使っている物では、全く同じ文字列が、同一であるという判定がなされず、
書かれた回数だけ組み込まれている模様。
設定にもよるのかな。

2014.10.1


特定の関数を書こうとすると開発ツール自体が必ず強制終了されてしまう問題が起こって焦ったが、
ncbファイルを除外したら、特に弊害も無く収まったみたい。

2014.9.27


IMEの無効化

ゲームなど作る際、そのままでは、半角/全角キーを押すと、キーを押すたびにウインドウの左上隅などに文字列が表示され、非常に鬱陶しい事になります。
これだけなら、半角/全角キーを押さなければ済む話だったのですが、環境によっては、全角/半角状態が全てのウインドウで共通となります。
これにより、他のウインドウで全角で作業していた場合に、ゲームのウインドウに移ると、そのまま全角状態になってしまいます。

これに対し、メインウインドウのプロシージャにWM_IME_SETCONTEXTが送られて来るので、こいつを拾って無視してやると、この問題をとりあえず解決できました。
原理は良くわかりません^^;。
WM_IME_STARTCOMPOSITIONでも無効化できますが、文字を変換するような手順を行うと、右下辺りに変換候補のウインドウが出て来てしまいます。
WM_IME_SETCONTEXTを無視した場合が、純粋に表示系をカットする感じなのに対し、こちらの場合は、WM_CHARに送られて来る文字列については、IMEを無視した形となります。
WM_IME_COMPOSITIONでも無効化できましたが、左上隅に空の変換バーのようなものが表示される事があります。
いずれの場合も、裏ではちゃんと稼働しているようで、ImmGetCompositionStringを使うと、入力途中の文字列を取得する事ができますし、IME系メッセージは送られ続けるようです。

一方、IME_SETOPENなどを使ってIMEを閉じるという方法も有りますが、逆に全角で作業していたウインドウに戻った際、半角になってて若干不便です。
また、扱いが難しいようで、いまいち思うように動いてくれませんでした。

2014.9.19


基底クラスオブジェクトから派生クラスオブジェクトへの代入

なぜか「基底から派生への代入は自分で組むしかない」と言われる事が多いようだが、実は大嘘。

派生クラスobj.基底クラス名::operator=(基底クラスobj)

で問題無く代入できる。
代入操作は、デフォルトでもoperator=という関数の形で定義されているため、基底クラスのこれを関数形式で呼び出すわけ。
他、

*(基底クラス名 *)&派生クラスobj=基底クラスobj

でもなぜか問題なく動くが、この場合は、派生クラスの所に、この基底クラスを持たないobjを指定した場合でも
コンパイルが通ってしまうため、潜在不具合の危険大(2014.3.23までは、前述の方法に気付かなかったため、
こちらを利用していたのだが、自分でもこの不具合を起こしてしまった)。

ともあれ、派生クラスのobjには、基底クラスのobjが(メンバobjと同様に)しっかり含まれてるのに、
それを参照する術が無いというのは謎であり、少し不便に思う。
obj.基底クラス名::thisみたいな事ができても良さそうなのに。
そういう必要がある場合は、基底でなくメンバにした方が良いのだろうか。

2013.5.21-2014.4.20


コンストラクタ・デストラクタの手動呼び出し

「コンストラクタから別のコンストラクタを呼び出す事は出来ない」と言われる事があるが、
クラス名::クラス名()という形でなら普通に呼び出せる様子。
クラス外からでもオブジェクト名.クラス名::クラス名()の形で呼び出せる。
デストラクタも同様に呼び出せるが、デストラクタの場合はthis->~クラス名()の形でも呼び出せる様子。
ただ、初期化の処理も行われ、constのメンバも変わってしまうので、かなり邪道かもしれない。

2014.3.23


引数の無いコンストラクタはデフォルトコンストラクタと呼ばれる。
自動的に作られるコピーコンストラクタは、デフォルトコピーコンストラクタと呼ばれる。
じゃあ自動的に作られるデフォルトコンストラクタは何だ?
デフォルトデフォルトコンストラクタか?

2014.3.23


切り詰め代入の警告漏れ

long型をshort型やchar型に直接代入しようとすると警告されるが、最後に+0などを付けると警告されない。
short型⇒char型の場合、途中に付けた場合でも警告されない。
unsigned long型からの場合は警告される。
signedとunsignedを比較しようとすると警告されるが、代入しようとした場合は警告されない。

2014.3.17-23


未初期化変数参照の警告漏れ

通常、値を割り当てられてない変数を参照しようとすると、ビルド時に警告される。
しかし、
int a;for(;;)if(a && (a=0));
あるいは
int a;while(1)if(a && (a=0));
のように書いた場合、6.0では警告が表示されない。

2014.3.7


派生クラスと基底クラスで同名の関数が有る場合、
「派生クラスで引数が同クラス」>「派生クラスで引数が基底クラス」>「基底クラスで引数が同クラス」
で優先される。

2013.10.16


関数の皮をかぶったマクロに注意

maxとminは関数の皮を被ったマクロなので要注意。
max(i++,5);とすると、iが2つ増えてしまう事がある。
計算に時間の掛かるような関数を入れるのも具合が悪い。
#include <windows.h>の前に#define NOMINMAXを入れて「リビルド」すれば定義されない様子。

CopyMemoryとZeroMemoryもマクロだが、実質的にはそれぞれmemsetとmemcpyを呼び出してるだけみたいだから害は無さそう。

2013.10.15


virtualの欠点

仮想デストラクタを持つクラスをZeroMemoryするのはアウト?
仮想デストラクタを持つ構造体をnewで取得し、構造体のサイズでZeroMemoryしてから
deleteすると、強制終了となる事が有った。
強制終了はデストラクタに入る前に起こる。デストラクタが空でも起こる。
基底クラスにはデストラクタ無し。

以下のサイトによると、仮想関数自体を含んでいる場合が問題らしい。
http://hexadrive.sblo.jp/article/29413823.html
便利な仮想関数だが、使用には注意を要する。

また、メンバ関数を実装していなくても、そのメンバ関数を使いさえしてなければ怒られないが、
virtual付きでメンバ関数を定義してしまうと、実装しなければ、そのオブジェクトを定義できなくなる。
これにより、同一のヘッダを複数のプログラムで参照している場合に障害になる事がある。

2013.5.29-2014.9.27


new[0]の非推奨

自分の環境では、仮想デストラクタを持つクラス/構造体を、
newで要素数0で取得すると、アドレスは格納されるが、
これに対してdeleteないしdelete[]を実行すると、ランタイムエラーが発生する。
この場合に限らず、要素数0でnew呼び出しは避けるのが無難か?

2013.5.21


ファイル入出力で低水準の方が余分な物が無い分早いかと思ってたら逆なのか。
高水準は高速化の為の工夫をしてくれてたらしい。
http://www5c.biglobe.ne.jp/~ecb/c/13_01.html
http://www5c.biglobe.ne.jp/~ecb/c/12_01.html
http://d.hatena.ne.jp/skyjoker/20130102/1357093289
低水準を使うなら、read/writeを変数ごとに呼び出すのでは無く、メモリに溜めこんで
一回で読み書きするのが得策なのかな。

2013.5.15


配列引数における注意

void func(char a[10])

のように、引数に配列を渡す場合、配列の参照が渡されてしまう。
すなわち、関数内で配列要素の値を変更すると、呼び出し元の配列まで変わってしまう
これにより、幾らa[10]という宣言をしていても、要素数がこれより少ない配列を渡してしまうと、
a[9]=5などとした場合にランタイムエラーが発生する事がある。

2013.5.21


派生と基底とデストラクタ

継承を持つクラスのオブジェクトを定義、またはnewで取得した場合、基底のコンストラクタ達が先に呼び出され、
次にメンバクラスのコンストラクタ、最後に自分のコンストラクタが呼び出される。
スコープアウトやdeleteによりオブジェクトが破棄された場合は逆に、自分のデストラクタが先に呼び出され、
次にメンバのデストラクタ、基底のデストラクタとなる。
(malloc〜freeの場合、どちらも呼び出されない)

しかし、newで取得した派生クラスのメンバのポインタを基底クラスに代入し、それをdeleteした場合、
基底のデストラクタ(および基底の持つメンバのデストラクタ)しか呼び出されない。
基底クラスのデストラクタの宣言の前にvirtualを付ける事により、その場合でも派生のデストラクタが
呼び出されるようになる。
派生の更に派生を作る場合、最も基底のものにさえvirtualが付いていれば、それも呼び出される様子だが…。
派生にしかvirtualが無い場合はdelete時に強制終了したりする。

コピーコンストラクタの場合、コピーコンストラクタは(代入演算子同様)自分の物のみが呼び出される。
デフォルトコピーコンストラクタは、全てのメンバクラス・基底クラスに対し、それぞれのコピーコンストラクタを呼び出す。
自分でコピーコンストラクタを作った際、自分で初期化の記述(≠代入)を行わないと、メンバや基底に対しては
デフォルトコンストラクタが呼び出される。

2013.5.21-2014.3.23


基底クラスのメンバを参照する場合、
基底クラス名::メンバ
とする。

2013.5.21


条件演算子を用いた参照に関する注意

参照を宣言する際、
基底クラス名 &ref=条件?派生クラスobj:派生クラスobj;

基底クラス名 &ref=条件?基底クラスobj:基底クラスobj;
なら問題無いが、
基底クラス名 &ref=条件?基底クラスobj:派生クラスobj;
とすると問題がある。これは、
基底クラス名 &ref=条件?基底クラスobj:(基底クラス名)派生クラスobj;
のように認識されるが、このキャストの際に、一時的なクラスオブジェクトが作成され、
目的のobjの参照では無くなってしまう。
基底クラス名 &ref=条件?基底クラスobj:*(基底クラス名 *)&派生クラスobj;
とすると一応大丈夫。

2013.5.21


オブジェクトを引数・戻り値で受け渡す方法の定義

クラス・構造体を引数として渡す時、及び戻り値として返す時、
コピーコンストラクタが呼び出されている。
コピーコンストラクタは
クラス名(const クラス名 &obj);
で定義できる。&が無いとコンパイルエラー。
constは無くてもコピーコンストラクタとして認識されるが、constなオブジェクトを渡せなくなる。
両方定義すると警告が発生。

2013.5.21


ゲームシステム関連の考察とか
ツクール関連
メモに戻る
トップに戻る