【C++】プライベートなコンストラクタを持つクラスをmake_uniqueする方法
クラス外ではインスタンス化されちゃあ困るけど、クラス内でインスタンス化したいとき。
普通にnew XXXってするとうまくいくんだけど、C++だとスマートポインタに置き換えたいよねってことで、std::make_uniqueとか使うよね。
つまりこう書きたい
エラーになる。ちなみにnew Aだと動く。
make_uniqueだと、一度クラスの外でnewでインスタンスを生成しているのが原因。
クラス外で一時的にnewによるインスタンス生成を認めるには継承を使って、非公開なコンストラクタにアクセスする。
GetInstanceメソッドを以下のように修正。
一時的に宣言した構造体(クラスでもおk)はローカルスコープ内に隠しているので問題なし。
確実に隠蔽するなら、GetInstanceメソッドの定義をヘッダではなくソースファイル側に書き、コンパイラにインライン展開を任せるといい感じにカプセル化できる。
ヘッダに定義があったらそれイジって生成し放題だからね。
【C++】簡単なvectorテクニック
メモ書きです。
std::remove_if
削除する項目を判断する関数を与えて、指定した要素を削除します。
これを読んだだけでは要素数は変化しません
削除すべき要素が末尾に集中するので、eraseメソッドでの処理が高速化できます
クラス、構造体で使う
条件式となる関数を与えるだけなので、クラスなどでも利用可能。
ただし、条件分岐に使う関数の引数にはそのベクタの要素となる型を指定すること。
要素の削除の高速化
eraseメソッドを使う場合だとは指定した要素を削除して、その後、その空白を埋めるために、先頭方向にずらす(コピーが発生する)ため、
要素数1000のvectorの先頭要素を削除すると、
2番目移行の要素全てずらすため、999回のコピーが発生します。
ソートが不要なvectorの場合。
この場合、削除したい項目を末尾に移動して削除するようにすると、
pop_backメソッドで削除できるため、削除にかかる処理がO(1)で済みます
ソートが必要なvectorで、先頭から削除する場合
この場合だと先程の方法はうまく使えません。削除のたびに位置が変わりますからね。
でもこの場合もかんたんです。
予め、vectorを逆順に並び替えて、末尾からアクセスしていけばいいだけです。
逆順に並び替えると、実際の先頭は末尾へ。実際の末尾は先頭へいくからです。
戻り値にvectorのインスタンスを返す場合。
この場合、ただ返すだけだと大量のコピーが発生して大変なことになります。(メモリも大量に消費します)
解決法としてはムーブセマンティクスを使います。
ムーブセマンティクスはこちらの方が書いてる記事が参考になるんじゃないかなぁと。
そしてこの方法はおそらくすべてのSTLで使えるんじゃないかなぁと。(全部は試してないので例外ありそうですが)
是非サンプルのmoveを外してただの戻り値にして、速度差を確認してくださいね
あ、僕はこの方法実用的なプログラムで使ったことないです。
いつも参照で解決してるので(笑)。
集中力切れた
集中切れちゃったので今回はこれまでとします。
他にはstd::countやsort、mergeなどもありますので興味あれば調べてくださいな
【C++】Zeromemoryは楽だが理解しないと危険
Zeromemoryマクロで少し面倒なことになったのでメモ。
ZeroMemoryマクロ、まあmemsetでもいいですが、指定した変数の中身を0クリアしてくれる使いやすいマクロです。
けど使い方を間違えると実行時エラーのもととなります。
以下のソースを動かしてみます。
#include<vector> #include<iostream> #include<Windows.h> using std::vector; using std::cout; using std::endl; struct Data1 { vector<int32_t> values; Data1(size_t size) { ZeroMemory(this, sizeof(Data1)); values.reserve(size); } }; int main(int32_t,char**) { Data1 d{5}; for (int8_t i = 0; i < 5; i++) { d.values.push_back(10 * i); cout << d.values.back() << endl; } }
Zeromemoryをよく知らない人からすれば一見問題なさそうな感じですが、
問題しかありません。
このソースを実行するとコンストラクタの「values.reserve(size);」でエラーが発生します。
Zeromemoryマクロを使ってクラスの中身全てを0クリアしているのが問題で、
vector内部で使われている変数まで0にしてしまいます。
vector内部でのみ使われているアクセス用のポインタのアドレスまで0にされているため、
正しくアクセスできず、エラーが発生します。
シンプルな回避策は以下となります
#include<vector> #include<iostream> #include<Windows.h> using std::vector; using std::cout; using std::endl; struct Data2 { vector<int32_t> *values; Data2(size_t size) { ZeroMemory(this, sizeof(Data2)); values = new vector<int32_t>; } }; int main(int32_t,char**) { Data2 d{5}; for (int8_t i = 0; i < 5; i++) { d.values->push_back(10 * i); cout << d.values->back() << endl; } }
この場合だとZeromemoryマクロを読んでも、Data2にあるvectorを参照するためのアドレスのみ初期化されて、その後にインスタンスを作っているので問題ありません。
あ、解放処理書いていないのでメモリリークしてますよ。
このエラー厄介なのがZeromemoryが呼ばれた場所ではなく、Zeromemoryによって初期化されたインスタンスが参照されたときに発生するので、Zeromemory書いたことを忘れてると、盲点になるんですよね。
ソースは自分。
【C++】演算子のオーバーロードの使い方(基礎)
演算子のオーバーロードについてかる~くゆる~く触れようと思います。
といっても私ならそれだけ言われてもわからないので実際に演算子のオーバーロードを使用するクラスを一緒に作っていきましょう。
基礎部分な学習なので、下手に混乱させないために最適化はしません。
最低限、コンストラクタとメソッドの区別がついていたらオーケーです。
以下のソースをコピペするなりしてください
#include<cstdio> class Point { public: int x; int y; Point(int _x, int _y) { this->x = _x; this->y = _y; } }; int main() { Point p1(10, 20); printf("%d:%d\n", p1.x, p1.y); }
これが一体何をしているのかわからないならクラスを少し勉強してきてから続きを読んでくださいね。
ではまず、このクラスに、別のクラスと比較して、X,Y両方の値が等しいかどうか調べるメソッドを書いてみます。演算子のオーバーロードをしない実装方法ですね
おそらくこんな感じになるでしょう
#include<cstdio> class Point { public: int x; int y; Point(int _x, int _y) { this->x = _x; this->y = _y; } bool Equals(Point _point) { if (this->x == _point.x && this->y == _point.y) { return true; } return false; } }; int main() { Point p1(10, 20); Point p2(10, 20); printf("p1 = %d:%d\n", p1.x, p1.y); printf("p2 = %d:%d\n", p2.x, p2.y); if (p1.Equals(p2)) { printf("true\n"); } else { printf("false\n"); } }
実際の呼び出しでは"p1.Equals(p2)"のようにしてますが、
p1 == p2
のようにしたほうが見やすくないですか?
ですが、こう書いてもコンパイルは通りません。
Equalsメソッドを書いてないのにEqualsメソッドを呼びだそうとしているようなものです。
なのでこのように書けるように演算子をオーバーロードして処理を書いて上げる必要があります。
それでは、無心でも良いので以下のソースをPointクラスのEqualsメソッドの下にでも書いてください
bool operator==(Point _point) { if (this->x == _point.x && this->y == _point.y) { return true; } return false; }
書いたら、
p1.Equals(p2)
を、
p1 == p2
にしてみてください。
動作するはずです。
動作が確認できたところで、解説します。
まず、operator の前にある bool が戻り値です。
演算結果が帰ってきます。
p1 == p2 だと
今回の例だと trueが帰ってくるので、
それが1f文で条件判定され、真と判定されます。
次の operator が演算子をオーバーロードするというキーワードです。これを書かないとオーバーロードできません。
その直後に書いてある、== がオーバーロードする演算子です。違和感しかないと思ったらメソッド名とでも思っておきましょう。
次の ()内の変数が引数です。注意なのが、演算子によって指定可能な引数の数が決まっていることです。
== は一つしか指定できないません。
次に{}内はその演算子での処理を記述します。今回だとEqualsメソッドと同じ動きですね。
さて、処理の記述方法、引数は一つまで、とわかったところですが、一つ問題にぶち当たります。
main関数の中身を以下のように書き換えてください。
具体的にはPoint変数が一つ増えてます。
int main() { Point p1(10, 20); Point p2(10, 20); Point p3(10, 20); printf("p1 = %d:%d\n", p1.x, p1.y); printf("p2 = %d:%d\n", p2.x, p2.y); if (p1==p2 && p1==p3) { printf("true\n"); } else { printf("false\n"); } }
3つ比較するならこう書けます
Equlasメソッドを使った場合はこう書きます。
p1.Equals(p2) == p1.Equals(p3)
私は嫌です。書くのが面倒くさいです。
ではもう一つ、Pointクラス同士を加算する処理を書いてみましょう。
以下の処理をPointクラスに追加してください。
Point operator+(Point _point) { //このクラスのコピーを作成 //コピーを作らないと元の値が変わってしまう。 Point result(*this); result.x += _point.x; result.y += _point.y; return result; }
それぞれの項目については先ほどの == メソッドと同じです。
実際にこれを使った処理をmain関数に書いてみましょう。
int main() { Point p1(10, 20); Point p2(10, 20); Point p3 = p1 + p2; printf("p1 = %d:%d\n", p1.x, p1.y); printf("p2 = %d:%d\n", p2.x, p2.y); printf("p3 = %d:%d\n", p3.x, p3.y); }
Pointクラスでの+演算子を定義したことで、Pointクラス同士の計算ができました。
以下の様な複数の計算も可能です。
Point p4 = p1 + p2 + p3;
具体的には、まず、
p1+p2 が処理されます。
p1+p2の結果、Point型の値が返ってくるので、それとp3を計算します。
どちらもPoint型なので正しく計算できます。
そしてすべての計算が終われば、変数p4に計算されたPoint型の値が代入されます。
これらのように演算子をオーバーロードして処理を書いてしまえば、いちいちメソッドを書くよりもより直感的に、なおかつシンプルに記述することができ、可読性が上がります。
別のクラス同士を計算したり比較することも自由にできるので、演算子を使って実装できそうな処理は演算子をオーバーロードして実装するといいでしょう。
【プログラミング】処理落ちに対応する考え方
実装されててアタリマエのことですが、されていないフリーゲームもちらほら見るので考え方とC言語でのサンプルだけちょっと紹介。
処理落ちに対応するには実に簡単で、小学校で習った「距離=速さ×時間」を使います。
この"時間"の部分ですが、ゲームであれば、1フレームごとに座標の更新(座標の再計算)が行われる処理が多いと思いますが、これは実に都合がよく、簡単に実装できます。
この場合での必要な要素は、
- 1秒間の移動量(速さ)
- 1フレームの処理時間(時間)
実際に見てもらったほうが早いと思うので
まずソースを貼ります
#include<Windows.h> void Loop(); void Update(); long long deltaTime = 0; int main() { Loop(); } void Update() { static float score = 0; static float scoreAdd = 100;//1秒で増えるスコア score += scoreAdd*(deltaTime / 1000.0f); } void Loop() { //無意味な無限ループ(サンプルなので適当) while (1) { static long long start = 0; static long long end = 0; //処理開始時間を記録 start = timeGetTime(); { //1フレーム内で行う処理 Update(); } //処理終了時間を記録 end = timeGetTime(); //終了時間 - 開始時間 = 1フレームにかかった処理時間 deltaTime = end - start; } }
Loop関数から見ていきましょう。
まず、座標の更新などの処理を始める前に、処理開始時の時間を取得します。
その後に必要な処理を全て行います。
処理が全て終わったら、処理終了時の時間を取得します。
このふたつを使ってサンプルのように計算することで、1フレームにかかった時間を取得することができます。
これで距離の計算に必要な"時間"が用意できましたね。
次にUpdate関数を見ていきましょう。
今回は座標計算ではないですが、1秒ごとにスコアが100ずつ増えていくようにしています。
このサンプルでは1フレームの時間(ミリ秒)がそのまま整数値となっているので、それを秒に直しています。
このプログラム例だと、
現在のスコア += (1秒に増える時間) × (1フレームの処理にかかった時間) です。
例えば、現在のスコアを0、1フレーム100ミリ秒かかったとすると、
0 += 100 × 0.1;
となり、スコアが10加算されます。
10FPSだとこれが1秒間に10回起きるわけですから、結果的に1秒で100加算されます。
ここで注意しておきたいのが、理由がない限り、少数が扱えるデータ型にしてください。
整数型にしてしまうと、切り捨てなどが起きてしまい計算がおかしくなります。
そんなことよりラーメンとじゃがバター食べたいですね。
【Android Studio】ほんとの0からカスタムビューを作成するよ
0カラってどこから?
A. プロジェクトの作成から
何言ってんだお前って言わないように。
タイトル詐欺って言わないように、ちゃんとやります。
勉強したはいいけど、実際に1から作るとしたらどうしていいかわからない人向けです。
知らない用語は調べてね。
今回使う環境はAndroid Studio2.1.1、APIレベルは19。2.1.0でもオーケー。ほかは知らない。
プロジェクトの作成
Android Studioを起動したら「Start new...」をクリックする。
次にアプリケーション名やらを決める。
基本的にいじるのは以下の二箇所
今回はこのまま"My Application"でやる。
次に使用するAPIを選択します。
スマホやタブレットの場合は一番上を選択。
今回はAPI 19を使用。今後支障出るかは知らないが、今回の目的に直接絡まないので他のレベルでもオーケー。
次にデフォルトのアクティビティを選択する。
今回は"Empty Activity"を選択
最後にアクティビティの名前を決めて終了。
そのままでオーケー
カスタムビューを実装する
今回のメイン課題です。頑張りましょう。
カスタムビューはゲームを作る際に便利というかほぼ使われるものじゃないかと思います。
クラスファイルの作成
カスタムビューのクラスファイルを作成します。
左側のフォルダツリーの"com.example..."となってるやつを右クリック。
このときグレーで(androidTest)や(test)と書かれていないものを選択するように!
そして[New] > [Java Class]と進んでください。
このようなポップアップダイアログが出てきますので、"MyView"と入力して確定してください。
するとクラスファイルがひとつ追加されました。
ここにカスタムビューを実装します。
まずカスタムビューを作るには"Viewクラス"を継承します
public class MyView extends View {
するとエラーが出ます。あらら。
Viewクラスを継承したら以下のコンストラクタを実装して継承元のViewクラスのコンストラクタを呼べるようにしてあげる必要があります。
public MyView(Context _c){ super(_c); } public MyView(Context _c, AttributeSet _attr){ super(_c,_attr); }
これでとりあえずエラーは消えます。
描画処理を実装する
とりあえず簡単な描画処理を実装します。
ぶっちゃけた話カスタムビューの話はここで終わってもいい気がするんだけど、それじゃあ内容が薄すぎるので。
一番簡単なonDrawメソッドをオーバーライドしたやり方です。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); }
適当に画面を塗りつぶすので以下のように修正してください。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.CYAN); }
黒や白だと本当に塗りつぶしているのか実感できないと思うので、シアンに設定してます。
これでカスタムビューの基盤ができました。
実行してみましょう。何も起きません。だってこのビューは定義しただけでどこでも使っていませんから。
カスタムビューを組み込む
カスタムビューを組み込むには二つの方法がありますが、両方説明するまでに集中力が持たないのでひとつだけ紹介。
javaソースから組み込む。
こちらの方法はプログラムの途中でもアクティビティ、ビューの切り替えが容易なやり方です。
組み込み方は簡単です。
以下のように"setContentView"でカスタムビューのインスタンスを渡すだけです
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); setContentView(new MyView(this)); }
これで実行してみてください。
シアンに塗りつぶされた画面が表示されます。
これでカスタムビューの基礎の基礎は終わりです。
あとはそれにタッチイベントを実装するなり新しいクラスを書くなり自由に応用してください。
【C++発展途上者向け】ゲッターとセッターは実装すべき?
ゲッターとセッターをどうやって実装するかという面白い問題を見つけたので個人的な考え方でも書いておきます
ゲッターはほとんど実装しない
私はほとんどゲッターを実装しません。
といっても面倒くさい時はカプセル化も何もしないんですけどね(笑)
ただガッツリ書くときは、そもそも直の値を取り出す必要のないプログラムを書きます。
ただ私の技量ではチョー難しく満足いくほどスムーズに実装できないのが現実ですね。
ちょっとした例を書いてみます。
ここに以下のようなクラスがありました
class Status { private: int hp; public: Status(int hp) { this->hp = hp; } int GetHpNow() { return this->hp; } int SetHpNow(int hp) { this->hp = hp; } };
このクラスを使って、hpが0になったら"死んだ"と表示するプログラムを書く事になりました。
何も考えずにゲッター・セッターを使うと素敵な糞プログラムが出来上がります。
int main() { Status status(50); int nowhp; //体力を100減らす nowhp = status.GetHpNow(); status.SetHpNow(nowhp - 100); //死んだか調べる if (status.GetHpNow() <= 0) { printf("死んだ\n"); } else { printf("生きてる\n"); } }
オブジェクト指向もくそもないですね。
じゃあもう少し賢く書いてみます。
ゲッター・セッターとバイバイする
今回の条件ならゲッターもセッターも
必要ありません。
これらではなく、明示的にhpを減らすメソッドと、生きてるかどうかを判定するメソッドを実装します。
実装するとこうなります。
class Status { private: int hp; public: Status(int hp) { this->hp = hp; } //HPを減らす void Damage(int damage) { this->hp -= damage; } //生きてたらtrueを返す bool Living() { return (this->hp > 0); } };
こうすればセッター・もゲッター・もいらないです。
これを実際に使って先ほどの糞プログラムを書き換えてみます。
int main() { Status status(50); //体力を100減らす status.Damage(100); //死んだか調べる if (status.Living()==true) { printf("生きてる\n"); } else { printf("死んだ\n"); } }
ドチャクソシンプルになりました。
むやみにゲッター・セッターを実装する前に、クラス内で完結させれるようなソースを実装できるようになるとシンプルになります。
できないものは実装するしかありませんが、ほとんどのことは工夫次第で実装せずに済むはずです。