-------------------------------------------①基本-------------------------------------------
■ポインタ①
int main() {
int a;
int* pA; //ポインタpAを宣言
a = 5;
pA = &a; //変数aのアドレスをpAに格納
cout << pA << "\n"; //a のアドレスを表示
cout << *pA; //a のアドレスに格納されている値を表示
*pA = 50; //*pA = aのアドレスを格納したpAに50を代入(aのアドレスに50を代入)
cout << a; //50
return 0;
}
・参照(結びつき)
int main()
{
int nums[] = { 10, 20, 30 };
int& r1 = nums[0]; //r1はnums[0]を参照(結びついている)
//ポインタ演算ではない
r1++; //nums[0]++;と同義
//「11」を表示
std::cout << nums[0] << std::endl; //r1++したことで、参照で結びついているnums[0]もインクリメント
std::cout << r1 << std::endl; //r1とnums[0]は結びついているため11が表示
}
・参照渡し
参照は「元のオブジェクトそのもの」です。
つまり、呼び出し元で指定した実引数と同じ扱いをします。
#include
void twice( int& a ) //引数として、vを受け取りaと結びつける
{
std::cout << a << std::endl; //aはvと結びつけられているため、vの値を出力
a *= 2; //vを2倍と同義
std::cout << a << std::endl; //vの2倍を出力
}
int main()
{
int v = 16;
std::cout << v << std::endl;
twice( v ); //twice関数に引数vを渡して実行
std::cout << v << std::endl; //twice関数終了後も、aと結びつけられているため、vの2倍を出力
}
・参照渡しで引数に配列を指定する場合
void showArr(int(&arr)[5]) //配列名を(&XXX)でくくり、配列数を[]に記載
{
int len = sizeof(arr) / sizeof(arr[0]); //配列総バイト(4X5=20)/配列1つ分(4)=5をlenに代入
for (int i = 0; i < len; i++) //5回ループ
{
std::cout << arr[i] << std::endl; //配列arrにnums配列の値を代入
}
}
int main()
{
int nums[] = { 1, 2, 3, 4, 5 };
showArr(nums); //showArr関数に引数として配列numsを渡す
}
・ポインタ渡し
ポインタ渡しは変数のメモリ上のアドレスを渡す記法である。
値渡しとは異なり、渡されたアドレスを間接参照する事で、
関数の呼び出し元の変数を書き換える事が出来る。
#include
void twice(int* a) //vのアドレスを受け取り、そのアドレスにaで値を代入
{
std::cout << *a << std::endl; //vのアドレス内の値を出力
*a *= 2; //
std::cout << *a << std::endl;
}
int main()
{
int v = 16;
std::cout << v << std::endl;
twice(&v); //vのアドレスを引数で渡す
std::cout << v << std::endl;
}
※ポインタ渡しを行う際は、nullチェックを行う
if (a == nullptr)
{
std::cout << "null" << std::endl;
return true;
}
■ヌルポインタ①
どんなオブジェクトや関数のアドレスと比較しても等しくならないポインタ
例えば、NULL か、NULL でないかで処理を分けることができる
アドレスを返すような関数では、関数が失敗したときは NULL を返すようにする。
NULL を渡すと何らかのデフォルトの処理をする関数を作る。
NULL を渡すと、そのパラメータを無視する。
このように、ヌルポインタはフラグ代わりに使用できる
■new①
new <型> とすればその型の変数が入るだけのメモリが確保される
結果は <型> へのポインタで受ける
使い終わったら必ず delete <アドレス>; でメモリを解放する
new に失敗すると NULL が返ってくる
int main()
{
int* p;
p = new int;
if(p == NULL) //メモリの確保に失敗したときはNULLが返される
return 0;
*p = 0;
cout << *p << endl;
delete p;
return 0;
}
ポインタは参照先がきちんとしていないと使ってはダメなはずだが、
newでメモリを動的に確保することで使える
(アドレスはnewが返してくれる)
必ずdeleteでメモリを解放する
※配列を確保したときは、delete の後に [ ] を付ける
■構造体①
<オブジェクト>.<メンバ> or
<アドレス> -> <メンバ名>
で構造体のメンバにアクセスできる
#define ELEM(array) (sizeof (array) / sizeof *(array))
struct SStudent
{
char szName[16];
int nJapanese;
int nMath;
int nEnglish;
};
void Disp(const SStudent* pstudent) //受け取った構造体の情報をポインタpstudentに格納
{
cout << "名前 : " << pstudent->szName << endl //ポインタなのでアロー"->"でメンバszNameにアクセス
<< " 国語 : " << pstudent->nJapanese << " 点, "
"数学 : " << pstudent->nMath << " 点, "
"英語 : " << pstudent->nEnglish << " 点" << endl;
}
int main()
{
SStudent students[] = {
{ "赤井孝", 73, 98, 86, }, //0番目要素 構造体と同じchar型,int型,int型,int型で格納する
{ "笠井大介", 64, 45, 40, }, //1番目要素
{ "吉田叶", 76, 78, 69, }, //2番目要素
};
int i;
for(i = 0; i < ELEM(students); i++) //構造体の要素数分ループ
Disp(&students[i]); //Disp関数に引数として、i番目の構造体をポインタ渡し
return 0;
}
■?①
<条件式> ? <真の場合> : <偽の場合>
int main()
{
int num1, num2;
cout << "2つ値を入力して下さい > ";
cin >> num1 >> num2;
cout << "大きい方の値は "
<< ((num1 > num2) ? num1 : num2) << " です。" << endl; //
return 0;
}
num1 > num2 のときに num1 を返すべきなのですから、条件文が満たされているときは
数値1を返すことが分かります。満たされていないときは数値2を返します。
■for文①
処理を繰り返す場合に使用
for(初期化の式; 繰り返し判断式; 変化のための式;){
文;
}
for(int i=1; i<=5; i++){
cout << "繰り返し"; //iが5になるまで繰り返される
}
・範囲の値を参照
int ar[] = {1, 2, 3, 4, 5, 6, 7}; //コンテナクラスでも使用可能
for(int x : ar) { //配列arの数だけ処理を繰り返す。
std::cout << x << "\n"; // 1 2 3・・・7 と順に表示される
}
・breakも使える
int ar[] = {3, 1, 4, 1, 5, 9};
for(auto x : ar) {
if( x == 4 )
break; // 4を見つけたらループ終了
}
※条件が成立したら途中で return も可能
■if文①
条件に当てはまる箇所を実行させたい場合
if (条件)
文1; //条件に当てはまるとき
else
文2; //条件に当てはまらないとき
}
■switch case①
値によって行う処理を指定したい場合う
switch(式){
case 1: //式が1の場合
文1;
break;
case 2: //式が2の場合
文2;
break;
default: //式がいずれにも当てはまらない場合
文3;
break;
}
■戻り値①
関数の呼び出し元に、関数から情報を返す
戻り値の型 関数名(引数リスト) //戻り値の型を指定
{
文;
return ; //値を関数の呼び出し元に返す
}
int buy(int x, int y) //渡された引数をx,yに代入して実行
{
int z;
z = x+y; //1+2を行う
return z; //戻り値として3を返す
}
int main()
{
int x = 1;
int y = 2;
int sum;
sum = buy(x, y); //引数1,2を渡して、結果をもらう(3をもらう)
return 0;
}
■while文①
条件に当てはまるまで繰り返し
while(条件){
文;
}
while(i <= 5){
cout << i; //条件にあてはまるまで繰り返し
i++;
}
-------------------------------------------②応用-------------------------------------------
■this②
メンバ関数を呼びだしたオブジェクトのアドレスを取得できる
代入演算子は *this を返すとよい
class CTest
{
int a[100];
public:
CTest* RetThis() { return this; }
};
int main()
{
CTest test1, test2;
cout << "test1 : " << &test1 << endl
<< "test2 : " << &test2 << endl
<< "test1 : " << test1.RetThis() << endl //test1のオブジェクトのポインタが返ってくる
<< "test2 : " << test2.RetThis() << endl; //test2のオブジェクトのポインタが返ってくる
return 0;
}
■インライン関数②
戻り値の型に inline を加えればインライン関数になる
インライン関数を使うと処理時間の短縮になる
inline void DispErrorMsg()
{
cout << "Error!" << endl;
}
■template②
型だけが違う沢山の関数を1つの実装にまとめるための機能
// 大きい方の値を返す関数(int 用)
int Max(int a, int b)
{
return (a > b) ? a : b;
}
// 大きい方の値を返す関数(unsigned int 用)
unsigned int Max(unsigned int a, unsigned int b)
{
return (a > b) ? a : b;
}
// 大きい方の値を返す関数(void* 用)
void* Max(void* a, void* b)
{
return (a > b) ? a : b;
}
→関数の中身はどれも全く一緒です。ただ単に戻り値、引数の型が異なるだけです。
関数定義を1つにまとめてしまおう、というのが関数テンプレート
template TYPE Max(TYPE a, TYPE b)
{
return (a > b) ? a : b;
}
TYPE は状況に応じて自由に変わる型だということを指定する
どれか1つは関数テンプレートの実装が見えていなければなりません。
基本的に関数テンプレートの実装は全てヘッダファイルに書きます。
・テンプレート引数
// メンバをint型として扱う
Position i_pos;
// メンバをfloat型として扱う
Position f_pos;
→テンプレート引数によって、扱う型が変わる
・テンプレートクラスの関数定義を行う場合
template
class Position
{
public:
Position();
private:
DATA m_PosX;
DATA m_PosY;
};
tmplate
Position::Position() //テンプレートクラスであることを明示
{
printf("Positionのコンストラクタ\n");
}
■try/catch/throw②
throw 文は「例外」を発生させるための命令
例外というものが try ブロック内で発生すると、catch ブロックに飛びます
例外が発生しなかった場合は catch ブロックは無視されます
int Func()
{
FILE *pf1 = NULL;
FILE *pf2 = NULL;
FILE *pf3 = NULL;
int fRet = 0;
try
{
pf1 = fopen(szFile1, "r");
if(pf1 == NULL)
throw 1;
pf2 = fopen(szFile2, "r");
if(pf2 == NULL)
throw 2;
pf3 = fopen(szFile3, "w");
if(pf3 == NULL)
throw 3;
Func2(pf1, pf2, pf3);
}
catch(int fError) //fError は throw で投げた値で初期化されます
{
fRet = fError;
}
if(pf1 != NULL)
fclose(pf1);
if(pf2 != NULL)
fclose(pf2);
if(pf3 != NULL)
fclose(pf3);
return fRet;
}
try ブロック内の関数の中で例外が投げられても catch できる
FILE* Open(const char* pszFile, const char* pszMode, int fError)
{
FILE *pf = fopen(pszFile, pszMode);
if(pf == NULL)
throw fError;
return pf;
}
int Func()
{
FILE *pf1 = NULL;
FILE *pf2 = NULL;
FILE *pf3 = NULL;
int fRet = 0;
try
{
pf1 = Open(szFile1, "r", 1);
pf2 = Open(szFile2, "r", 2);
pf3 = Open(szFile3, "w", 3);
Func2(pf1, pf2, pf3);
}
catch(int fError) //同じ型を受け取る引数を指定、複数指定可
{
fRet = fError;
}
if(pf1 != NULL)
fclose(pf1);
if(pf2 != NULL)
fclose(pf2);
if(pf3 != NULL)
fclose(pf3);
return fRet;
}
■継承②
protected: を使うと、外部には公開されないが派生先には公開される。
オーバーライド: 派生元の関数を派生先で再定義すること
■virtual②
仮想関数にすると、派生元の関数からでも適切なオーバーライド関数を呼びだしてくれるようになる
virtualキーワードを付けることで、virtualの関数がポインタを通して呼ばれた時に、
コンパイラはそのポインタ先のオブジェクトを見てどのクラスの関数を呼ぶかを決めるようになる
詳しくは不明
■アロー演算子②
ポインタ変数からのメンバ関数呼び出し時に使用
※通常のメンバ関数の場合: t.func()
ポインタ変数のメンバ関数の場合: t->func()
int main(int argc, char* argv[])
{
Television *tvp; // テレビクラスのポインタの宣言
tvp = new Television(); // テレビクラスのオブジェクトをヒープ領域に確保
// これにより、ポインタの指す先が決まる。
// ここでコンストラクタが呼ばれる
tvp->printStatus();
tvp->setPower(1);
tvp->setChannel(8);
tvp->setVolume(10);
tvp->printStatus();
delete tvp; // new で確保したオブジェクトは delete で解放
return 0;
}
■enum(列挙型)②
列挙型とは、ざっくり言ってしまえば、「選択肢」を表す整数の定数を定義するための変数型
選択肢以外の値は入れられない 型(列挙型)の変数が定義できるようになります
enum EDirection { E_Left = 0, E_Right = 1, };
■extern②
他のモジュールで定義されている関数を参照するには、
参照元のソースでその関数がextern宣言されていなければならない。
extern int input(char *);
■static②
付加された変数または関数が静的であることを宣言
・static関数
同プロジェクト内の複数ファイルに同じ関数名,引数の
関数が存在する場合、エラーとなる。
このエラーを避けるために、どちらかの関数の前にstaticを付ける。
static int func()
すると、 int func()はそのファイル内だけのローカルな関数となり、
他のファイルの同一関数名,引数の関数とバッティングすることはなくなる。
・staticグローバル変数
同プロジェクト内の複数ファイルで同一名称のグローバル変数が
定義されていると、エラーとなる。
このエラーを避けるために、どちらかのグローバル変数の前にstaticを付ける。
static int g_var = 1;
すると、関数同様にバッティングすることはなくなる。
・staticローカル変数
ローカル変数の有効範囲を抜けても、変数の値が保持される。
何度も関数内に入ったり、ループさせた場合に変化が出てくる。
関数を抜けても情報はそのまま残るが、スコープの範囲外からは
変数を参照することはできない。
・staticメンバ変数
オブジェクトが保持するのではなく、クラスが保持することになる。
・staticメンバ関数
オブジェクトが保持するのではなく、クラスが保持することになる。
各オブジェクトが保持する通常のメンバ変数にはアクセス出来なくなる。
■operator②
「演算子オーバーロード」とは、+, -, *, / などの演算子を再実装することで、
式を楽に、しかも分かりやすく記述するための仕組み
operator+(const T&, const T&)
準備中
■auto②
変数宣言時に具体的な型名のかわりに auto キーワードを指定する事によって、
変数の型を初期化子から推論できる。
auto i = 0; //iはint型で推論
const auto l = 0L; //iはconst long型で推論
auto& r = i; //rはint&型で推論
■キャスト②
型を変換することを明示的に記載
(型名) 式
inum = (int)dnum; //dnumの値をint型に変換後、inumに代入
■sizeof②
データ型のサイズ(バイト)を求める単項演算子です。
sizeofは、原則、コンパイル時に計算されます。
sizeof (変数) //変数のバイト数を求める
・配列のサイズを調べる
using namespace std;
int main(int argc, char const* argv[])
{
int i = 0;
int a[3] = {0};
char str[] = "hoge";
cout << "i = " << sizeof (i) << endl;
cout << "a[] = " << sizeof (a) << endl;
cout << "str = " << sizeof (str) << endl;
return 0;
}
実行結果:
i = 4 //iはint型で4バイト
a[] = 12 //int型の配列が3つで4x3=12バイト
str = 5 //4文字+NULL文字文で5バイト
・配列の要素数を調べる
int main(int argc, char const* argv[])
{
int a[] = { 1,2,3 }; //int型の3要素の配列aを定義
unsigned int size = sizeof(a) / sizeof(int); //int型の3要素の配列aのバイト数12/int型4バイトで割る
cout << size << endl; //sizeは12/4で3となる。
return 0;
}
-------------------------------------------③用語-------------------------------------------
■wrap(ラッパー関数)③
ある関数を呼び出す際に、引数等の設定を行わないといけない場合に、
ラッパー関数を呼び出して、設定をしてから指定の関数を呼び出すときに
使う関数のこと。
準備中
■handle(ハンドル)③
準備中
-------------------------------------------④マクロ-------------------------------------------
■#ifdef/#endif④
機能のON/OFFに使われる
#define DEBUG
...
#ifdef DEBUG
cout << "Debug: hensuu = " << hensuu << endl;
#endif
→DEBUG というマクロが定義されていた場合に、
#ifdefと#endifの間が有効になる。
※#ifndefはDEBUGが定義されていない場合に有効になる。
■#ifndef/#endif④
二重定義防止コード
#ifndef <固有なマクロ名>
#define <固有なマクロ名>
...宣言...
#endif
#ifndef D_CommandLineArguments_H //名前はなんでもいい
#define D_CommandLineArguments_H //名前はなんでもいい
...
#endif
■マクロ④
頻出するものをマクロで1つにまとめ、さらに改造を容易にできる
マクロで数値を意味のある文字に置き換えられる。
#define <マクロ名> <差し込むテキスト>
//FUNC を void Func(int x, int y) に置き換える
#include
using namespace std;
#define FUNC void Func(int x, int y)
FUNC;
int main()
Func(1, 2);
Func(158, 144);
return 0;
}
FUNC
{
cout << "2 * " << x << " + " << y
<< " = " << 2 * x + y << endl;
}
//引数を持たせることも可能
#include
using namespace std;
#define FUNC(name) void name(int x, int y)
FUNC(Func);
int main()
{
Func(1, 2);
Func(158, 144);
return 0;
}
FUNC(Func)
{
cout << "2 * " << x << " + " << y
<< " = " << 2 * x + y << endl;
}
■定義済みマクロ④
ソース中で定義しなくても利用することができる。
例:
__DATE__ ‐ コンパイル時の日付。"Mmm dd yyyy" 形式の文字列(例、"Oct 10 2010")
__FILE__ ‐ コンパイルされるソースファイルのファイル名
__LINE__ ‐ 現在の行番号。10進数の定数で表わされる
__TIME__ ‐ ソースのコンパイル時刻。"hh:mm:ss" 形式の文字列 (例、"01:23:45")
__func__ ‐ 関数名が得られる
-------------------------------------------⑤コンテナ関連-------------------------------------------
■std::array⑤
固定長配列のコンテナクラス。
std::vector の size() や back() などの便利な機能が使えない。
それらを使いたいけど、サイズは固定でいいので vector を使うほどでもない。
ってときは std::array の出番。
std::array<型, 要素数> オブジェクト名; で宣言
std::array ar; // int型、要素数10
・配列が空か判定する関数: empty()
if( !ar.empty() ) {
// ar が空でなければ、なんらかの処理を行う
}
■std::vector(コンテナ)⑤
コンテナ: 複数のデータを格納し、管理するオブジェクト
vector で可変長配列が使える(簡単に長さを変えられる配列を使える)
vector はクラステンプレート
#include
#include //vector使用時はインクルードする
要素指定有り(要素の初期化):
std::vector<データ型> 変数名(要素数, 初期値)
std::vector iv(100, 5); // 要素数100、初期値5
動的配列を確保しているかどうかの判定:
empty
データ追加(末尾追加):
push_back
データ削除(末尾削除)
pop_back
int main()
{
int i;
std::vector vi(10, 0);
//std::cout << vi.size() << std::endl;
// 配列の末尾に5を追加
vi.push_back(5); //配列末尾(11番目の配列)に5を追加
for(i = vi.size(); i == vi.size(); i++)
std::cout << vi[10] << std::endl; //11番目の配列の値を出力
vi.pop_back(); //配列末尾(11番目の配列)を削除
for (i = vi.size(); i == vi.size(); i++)
std::cout << vi[10] << std::endl; //11番目の配列は削除されたのでエラーになる
return 0;
}
例:
using namespace std;
int main()
{
vector vct(10); //int型のテンプレート引数10を渡すことで、10の配列を作成
int i; //vct(10) の (10)は指定しなくてもよい
for(i = 0; i < vct.size(); i++) //vct.sizeで指定した10分ループで"初期化"
vct[i] = 0;
for(i = 0; i < vct.size(); i++)
cout << vct[i] << ' ';
cout << endl;
vct.resize(5); //vct.resizeで配列数を変更
for(i = 0; i < vct.size(); i++)
vct[i] = 1;
for(i = 0; i < vct.size(); i++)
cout << vct[i] << ' ';
cout << endl;
return 0;
}
・vectorを連結
連結対象ベクター名.insert(連結対象ベクター開始位置, 連結する値の開始位置(ここから), 連結する値の終了位置(ここまで連結));
#include
#include
using namespace std;
int main(int argc, char const* argv[])
{
vector v1{ 1,2,3 }, v2{ 4,5,6 };
v1.insert(v1.end(), v2.begin(), v2.end()); //v1の末尾からv2を全て連結
for (auto i : v1) {
cout << i << " ";
}
cout << endl;
return 0;
}
・値の参照、代入
std::vector data{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; //指定した値で初期化
for (int i = 0; i < 10; ++i)
std::cout << data[i]; // data の i 番目の要素を表示
・at で参照、代入
std::vector data{3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
for (int i = 0; i < 10; ++i)
std::cout << data.at(i); // data の i 番目の要素を表示
実行結果:1 2 3 4 5 6
■std::iota⑤
連番でコンテナや配列を指定したレンジで埋める機能。
"連番"の数字でコンテナや配列を初期化する場合には、std::iotaが非常に簡単。
#include
#include
#include //std::iota使用時にインクルード
using namespace std;
int main(int argc, char const* argv[])
{
vector v(10);
iota(v.begin(), v.end(), 1); //vの先頭から末尾まで、開始1から10まで順番に初期化
for (auto i : v) {
cout << i << " ";
}
cout << endl;
return 0;
}
実行結果:1 2 3 4 5 6 7 8 9 10
■イテレーター⑤
iterator: どのデータ型にも対応可能なポインタ
vectorやlistなどのコンテナオブジェクトは様々なデータ型を格納しています。
それらのデータ型にとらわれずデータを扱う方法がこのイテレータを使用した方法です。
std::vector<データ型>::iterator 変数名;
std::vector vi(10, 0);
std::vector::iterator first, end, itr;
配列の最初を取得する:
begin
配列の最後を取得する:
end
イテレータのbeginからendまでは連続したアドレスになっているので、
beginの位置は[0]、beginの位置 + 1は[1]と考えることができます。
また、イテレータに内包されているデータにアクセスする場合は
変数の前に「*」をつけることで参照等が可能です。
++itr; で次の要素へ移動
--itr; で前の要素へ移動
vector.insert(イテレータ,値); で要素を挿入
vec.insert(vec.begin() + 1, 4);
int main() {
std::vector vi(10, 0);
std::vector::iterator first, end, itr;
first = vi.begin();
end = vi.end();
for (itr = first; itr != end; itr++) //最初から最後まで繰り返し
{
*itr = 1000; //itrの中身に1000を代入
}
for (itr = first; itr != end; itr++)
{ std::cout << *itr << std::endl; //itrの中身を出力
}
}
autoを使うことでイテレータの長い変数名を大幅に短縮することが可能であり、
変数自体は通常のイテレータと同様に扱うことができます。
int main() {
std::vector vi(10, 0);
for (auto itr = vi.begin(); itr != vi.end(); itr++) //autoで型を推論してくれる
{
*itr = 10;
}
for (auto itr = vi.begin(); itr != vi.end(); itr++)
{
std::cout << *itr << std::endl;
}
}
任意位置挿入: 引数で指定した位置にデータを挿入する
insert
戻り値:
iterator:
挿入したデータのiterator
引数:
第一引数:
iterator:
挿入位置
第二引数:
挿入データ
std::vector vi(10, 0);
// 配列[1]に100を追加
std::vector::iterator i = vi.insert(vi.begin() + 1, 100); //vi.begin() + 1が挿入位置
std::cout << *i << std::endl; //100が挿入データ
任意位置削除: 配列のデータを削除する
erase
戻り値:
削除した次の位置のiterator
引数:
その①.
iterator:
削除位置
その②.
iterator first:
削除位置の開始
iterator end:
削除位置の終端
int main() {
std::vector vi(10, 0);
vi.erase(vi.begin()); // 配列の先頭[0]を削除
vi.erase(vi.begin() + 2, vi.begin() + 5); //vi.begin()+2から5までの間を削除
for (auto itr = vi.begin(); itr != vi.end(); itr++)
{
std::cout << *itr << std::endl;
}
}
※erase()はコンテナの内容をクリアするだけで、メモリは解放されない
メモリを解放するには、 "shrink_to_fit()"を使用
vi.shrink_to_fit(); //メモリ開放
・コンテナの要素を全て削除
vi.clear();
■std::queue(キュー)⑤
FIFO(ファーストイン ファーストアウト)のキューを実現するコンテナ
std::queue<データ型> 変数名;
キューは、最初に入れられたものが最初に取り出されます。
先入れ先だしのリスト構造でデータを保持します。
取り出された値は、キューから削除されます。
キューへのデータ追加は、キューの一番後ろです。
#include
#include
using namespace std;
int
main(int argc, char const* argv[])
{
queue q1;
q1.push (1); //pushで列の後ろにデータを追加する
q1.push (2); //pushで列の後ろにデータを追加する
q1.push (3); //pushで列の後ろにデータを追加する
cout << q1.front() << endl; //先頭のデータを取り出す キューからは削除されない
q1.pop(); //popで先頭のデータを取り出す キューから削除される
cout << q1.front() << endl;
q1.pop();
cout << q1.front() << endl;
q1.pop();
return 0;
}
実行結果:
1
2
3
キューが空かどうか確認: empty()
cout << "q1: " << q1.empty() << endl; // 0 空じゃない場合
cout << "q2: " << q2.empty() << endl; // 1 空の場合
■std::stack(スタック)⑤
LIFO(ラストイン ラストアウト)のスタックを実現するコンテナ
キューの逆で最初に入れられたものが最後に取り出される
#include
#include
int main() {
std::stack st;
st.push(3);
st.push(4);
std::cout << st.top() << std::endl; // 4
st.pop(); // 後に入れられた4を削除
std::cout << st.top() << std::endl; // 3
return 0;
}
実行結果:
4
3
■std::fill⑤
配列やコンテナに対して、レンジ指定をして、すべて同じ値を代入します。
std::fill(代入開始位置, 代入終了位置, 代入値);
#include
#include
//#include
using namespace std;
int main(int argc, char const* argv[])
{
vector v(10); //10の要素を持つベクターコンテナを宣言
std::fill(v.begin(), v.end(), 3); //ベクターvの最初から最後まで3を代入
for (auto i : v) {
cout << i << " ";
}
cout << endl;
return 0;
}
■std::copy⑤
指定範囲の要素をコピーするアルゴリズムです。
std::vectorをコピーするときなどに使われます。
std::copyは、先頭から順番にコピーします。
std::reverse_copyで、コピー元と逆向きにコピーします。
std::copy(first, end, result); //firstからendまでをresultにコピー
■std::list⑤
http://vivi.dyndns.org/tech/cpp/list.html
準備中
-------------------------------------------⑥thread-------------------------------------------
■std::thread⑥
スレッドを複数作成した場合、プログラムが複数同時に実行される。
#include
#include //thread使用時にインクルード
void worker() { //ワーカスレッド
std::cout << "thread id: " << std::this_thread::get_id() << std::endl; //get_id()でスレッドID取得
}
int main() {
std::thread th(worker); //std::thread スレッド名(動かすスレッド)でthread起動
th.join(); //スレッド名.join でthreadの終了を待つ
return 0;
}
・引数が必要な場合
std::thread th(worker, 5, 10);
・参照を渡す場合
std::thread th(worker, std::ref(value));
・threadの切り離し
実行スレッドをスレッドオブジェクトから分離して、実行を独立して続行できるようにします。
割り当てられたリソースは、スレッドが終了すると解放されます。
th.detach();
・threadがアクティブか判定
th.joinable() //アクティブならtrue/非アクティブならfalse
・別のthreadと入れ替える
int main()
{
std::thread t1([]{ /*...*/ });
std::thread t2;
t1.swap(t2); //t1スレッドとt2スレッドを入れ替える
t2.join();
return 0;
}
準備中
■std::mutex⑥
C++で同時に複数のスレッドによってアクセスされる共有データを保護するために使用
mutexを使用してロックとアンロックを行うことで、データを安全に操作できます。
準備中
-------------------------------------------⑦std(ライブラリ)関連-------------------------------------------
■アルゴリズムライブラリ⑦
https://ja.cppreference.com/w/cpp/algorithm
準備中
■boost⑦
準備中
■std::swap⑦
2つのオブジェクトの値を入れ替える、テンプレート関数
#include
#include //swap使用時にインクルード
int main(int argc, char* argv[]) {
int x = 10, y = 20;
std::swap(x, y); //xとyの値を入れ替える
std::cout
<< "x=" << x
<< " y=" << y
<< std::endl;
return 0;
}
-------------------------------------------⑧単体テスト関連-------------------------------------------
■単体テストの仕組み⑧
#include "CppUTest/CommandLineTestRunner.h"
#include "TestTarget.h"
#include //テストに必要なファイルをインクルード
// テストグループを定義
TEST_GROUP(TestFuncGroup)
{
//テストを実行、成功させるために必要な状態や前提条件の集合の準備
TEST_SETUP() // 各テストケースの実行直前に呼ばれる仮想メソッド
{
std::cout << " TEST_SETUP called" << std::endl;
}
TEST_TEARDOWN() // 各テストケースの実行直後に呼ばれる仮想メソッド
{
std::cout << " TEST_TEARDOWN called" << std::endl;
}
};
// テストを実行するメソッド
TEST(TestFuncGroup, TestFunc2)
{
std::cout << "TestFunc2" << std::endl;
int ret = TestFunc2(10);
CHECK_EQUAL(10, ret);
}
-------------------------------------------⑨その他-------------------------------------------
■メモリが取れなくなる⑨
int main() {
int* p = nullptr;
try {
p = new int[100000000]; //動的にメモリを確保してしまう
p = new int[100000000];
p = new int[100000000];
p = new int[100000000];
p = new int[100000000];
std::cout << p << std::endl;
}
catch (const std::bad_alloc& e) { //メモリが確保できなかった場合エラーとなる
std::cout << "cannot be allocated." << std::endl;
}
delete p;
return 0;
}
■ポインタ①
int main() {
int a;
int* pA; //ポインタpAを宣言
a = 5;
pA = &a; //変数aのアドレスをpAに格納
cout << pA << "\n"; //a のアドレスを表示
cout << *pA; //a のアドレスに格納されている値を表示
*pA = 50; //*pA = aのアドレスを格納したpAに50を代入(aのアドレスに50を代入)
cout << a; //50
return 0;
}
・参照(結びつき)
int main()
{
int nums[] = { 10, 20, 30 };
int& r1 = nums[0]; //r1はnums[0]を参照(結びついている)
//ポインタ演算ではない
r1++; //nums[0]++;と同義
//「11」を表示
std::cout << nums[0] << std::endl; //r1++したことで、参照で結びついているnums[0]もインクリメント
std::cout << r1 << std::endl; //r1とnums[0]は結びついているため11が表示
}
・参照渡し
参照は「元のオブジェクトそのもの」です。
つまり、呼び出し元で指定した実引数と同じ扱いをします。
#include
void twice( int& a ) //引数として、vを受け取りaと結びつける
{
std::cout << a << std::endl; //aはvと結びつけられているため、vの値を出力
a *= 2; //vを2倍と同義
std::cout << a << std::endl; //vの2倍を出力
}
int main()
{
int v = 16;
std::cout << v << std::endl;
twice( v ); //twice関数に引数vを渡して実行
std::cout << v << std::endl; //twice関数終了後も、aと結びつけられているため、vの2倍を出力
}
・参照渡しで引数に配列を指定する場合
void showArr(int(&arr)[5]) //配列名を(&XXX)でくくり、配列数を[]に記載
{
int len = sizeof(arr) / sizeof(arr[0]); //配列総バイト(4X5=20)/配列1つ分(4)=5をlenに代入
for (int i = 0; i < len; i++) //5回ループ
{
std::cout << arr[i] << std::endl; //配列arrにnums配列の値を代入
}
}
int main()
{
int nums[] = { 1, 2, 3, 4, 5 };
showArr(nums); //showArr関数に引数として配列numsを渡す
}
・ポインタ渡し
ポインタ渡しは変数のメモリ上のアドレスを渡す記法である。
値渡しとは異なり、渡されたアドレスを間接参照する事で、
関数の呼び出し元の変数を書き換える事が出来る。
#include
void twice(int* a) //vのアドレスを受け取り、そのアドレスにaで値を代入
{
std::cout << *a << std::endl; //vのアドレス内の値を出力
*a *= 2; //
std::cout << *a << std::endl;
}
int main()
{
int v = 16;
std::cout << v << std::endl;
twice(&v); //vのアドレスを引数で渡す
std::cout << v << std::endl;
}
※ポインタ渡しを行う際は、nullチェックを行う
if (a == nullptr)
{
std::cout << "null" << std::endl;
return true;
}
■ヌルポインタ①
どんなオブジェクトや関数のアドレスと比較しても等しくならないポインタ
例えば、NULL か、NULL でないかで処理を分けることができる
アドレスを返すような関数では、関数が失敗したときは NULL を返すようにする。
NULL を渡すと何らかのデフォルトの処理をする関数を作る。
NULL を渡すと、そのパラメータを無視する。
このように、ヌルポインタはフラグ代わりに使用できる
■new①
new <型> とすればその型の変数が入るだけのメモリが確保される
結果は <型> へのポインタで受ける
使い終わったら必ず delete <アドレス>; でメモリを解放する
new に失敗すると NULL が返ってくる
int main()
{
int* p;
p = new int;
if(p == NULL) //メモリの確保に失敗したときはNULLが返される
return 0;
*p = 0;
cout << *p << endl;
delete p;
return 0;
}
ポインタは参照先がきちんとしていないと使ってはダメなはずだが、
newでメモリを動的に確保することで使える
(アドレスはnewが返してくれる)
必ずdeleteでメモリを解放する
※配列を確保したときは、delete の後に [ ] を付ける
■構造体①
<オブジェクト>.<メンバ> or
<アドレス> -> <メンバ名>
で構造体のメンバにアクセスできる
#define ELEM(array) (sizeof (array) / sizeof *(array))
struct SStudent
{
char szName[16];
int nJapanese;
int nMath;
int nEnglish;
};
void Disp(const SStudent* pstudent) //受け取った構造体の情報をポインタpstudentに格納
{
cout << "名前 : " << pstudent->szName << endl //ポインタなのでアロー"->"でメンバszNameにアクセス
<< " 国語 : " << pstudent->nJapanese << " 点, "
"数学 : " << pstudent->nMath << " 点, "
"英語 : " << pstudent->nEnglish << " 点" << endl;
}
int main()
{
SStudent students[] = {
{ "赤井孝", 73, 98, 86, }, //0番目要素 構造体と同じchar型,int型,int型,int型で格納する
{ "笠井大介", 64, 45, 40, }, //1番目要素
{ "吉田叶", 76, 78, 69, }, //2番目要素
};
int i;
for(i = 0; i < ELEM(students); i++) //構造体の要素数分ループ
Disp(&students[i]); //Disp関数に引数として、i番目の構造体をポインタ渡し
return 0;
}
■?①
<条件式> ? <真の場合> : <偽の場合>
int main()
{
int num1, num2;
cout << "2つ値を入力して下さい > ";
cin >> num1 >> num2;
cout << "大きい方の値は "
<< ((num1 > num2) ? num1 : num2) << " です。" << endl; //
return 0;
}
num1 > num2 のときに num1 を返すべきなのですから、条件文が満たされているときは
数値1を返すことが分かります。満たされていないときは数値2を返します。
■for文①
処理を繰り返す場合に使用
for(初期化の式; 繰り返し判断式; 変化のための式;){
文;
}
for(int i=1; i<=5; i++){
cout << "繰り返し"; //iが5になるまで繰り返される
}
・範囲の値を参照
int ar[] = {1, 2, 3, 4, 5, 6, 7}; //コンテナクラスでも使用可能
for(int x : ar) { //配列arの数だけ処理を繰り返す。
std::cout << x << "\n"; // 1 2 3・・・7 と順に表示される
}
・breakも使える
int ar[] = {3, 1, 4, 1, 5, 9};
for(auto x : ar) {
if( x == 4 )
break; // 4を見つけたらループ終了
}
※条件が成立したら途中で return も可能
■if文①
条件に当てはまる箇所を実行させたい場合
if (条件)
文1; //条件に当てはまるとき
else
文2; //条件に当てはまらないとき
}
■switch case①
値によって行う処理を指定したい場合う
switch(式){
case 1: //式が1の場合
文1;
break;
case 2: //式が2の場合
文2;
break;
default: //式がいずれにも当てはまらない場合
文3;
break;
}
■戻り値①
関数の呼び出し元に、関数から情報を返す
戻り値の型 関数名(引数リスト) //戻り値の型を指定
{
文;
return ; //値を関数の呼び出し元に返す
}
int buy(int x, int y) //渡された引数をx,yに代入して実行
{
int z;
z = x+y; //1+2を行う
return z; //戻り値として3を返す
}
int main()
{
int x = 1;
int y = 2;
int sum;
sum = buy(x, y); //引数1,2を渡して、結果をもらう(3をもらう)
return 0;
}
■while文①
条件に当てはまるまで繰り返し
while(条件){
文;
}
while(i <= 5){
cout << i; //条件にあてはまるまで繰り返し
i++;
}
-------------------------------------------②応用-------------------------------------------
■this②
メンバ関数を呼びだしたオブジェクトのアドレスを取得できる
代入演算子は *this を返すとよい
class CTest
{
int a[100];
public:
CTest* RetThis() { return this; }
};
int main()
{
CTest test1, test2;
cout << "test1 : " << &test1 << endl
<< "test2 : " << &test2 << endl
<< "test1 : " << test1.RetThis() << endl //test1のオブジェクトのポインタが返ってくる
<< "test2 : " << test2.RetThis() << endl; //test2のオブジェクトのポインタが返ってくる
return 0;
}
■インライン関数②
戻り値の型に inline を加えればインライン関数になる
インライン関数を使うと処理時間の短縮になる
inline void DispErrorMsg()
{
cout << "Error!" << endl;
}
■template②
型だけが違う沢山の関数を1つの実装にまとめるための機能
// 大きい方の値を返す関数(int 用)
int Max(int a, int b)
{
return (a > b) ? a : b;
}
// 大きい方の値を返す関数(unsigned int 用)
unsigned int Max(unsigned int a, unsigned int b)
{
return (a > b) ? a : b;
}
// 大きい方の値を返す関数(void* 用)
void* Max(void* a, void* b)
{
return (a > b) ? a : b;
}
→関数の中身はどれも全く一緒です。ただ単に戻り値、引数の型が異なるだけです。
関数定義を1つにまとめてしまおう、というのが関数テンプレート
template
{
return (a > b) ? a : b;
}
TYPE は状況に応じて自由に変わる型だということを指定する
どれか1つは関数テンプレートの実装が見えていなければなりません。
基本的に関数テンプレートの実装は全てヘッダファイルに書きます。
・テンプレート引数
// メンバをint型として扱う
Position
// メンバをfloat型として扱う
Position
→テンプレート引数によって、扱う型が変わる
・テンプレートクラスの関数定義を行う場合
template
class Position
{
public:
Position();
private:
DATA m_PosX;
DATA m_PosY;
};
tmplate
Position
{
printf("Positionのコンストラクタ\n");
}
■try/catch/throw②
throw 文は「例外」を発生させるための命令
例外というものが try ブロック内で発生すると、catch ブロックに飛びます
例外が発生しなかった場合は catch ブロックは無視されます
int Func()
{
FILE *pf1 = NULL;
FILE *pf2 = NULL;
FILE *pf3 = NULL;
int fRet = 0;
try
{
pf1 = fopen(szFile1, "r");
if(pf1 == NULL)
throw 1;
pf2 = fopen(szFile2, "r");
if(pf2 == NULL)
throw 2;
pf3 = fopen(szFile3, "w");
if(pf3 == NULL)
throw 3;
Func2(pf1, pf2, pf3);
}
catch(int fError) //fError は throw で投げた値で初期化されます
{
fRet = fError;
}
if(pf1 != NULL)
fclose(pf1);
if(pf2 != NULL)
fclose(pf2);
if(pf3 != NULL)
fclose(pf3);
return fRet;
}
try ブロック内の関数の中で例外が投げられても catch できる
FILE* Open(const char* pszFile, const char* pszMode, int fError)
{
FILE *pf = fopen(pszFile, pszMode);
if(pf == NULL)
throw fError;
return pf;
}
int Func()
{
FILE *pf1 = NULL;
FILE *pf2 = NULL;
FILE *pf3 = NULL;
int fRet = 0;
try
{
pf1 = Open(szFile1, "r", 1);
pf2 = Open(szFile2, "r", 2);
pf3 = Open(szFile3, "w", 3);
Func2(pf1, pf2, pf3);
}
catch(int fError) //同じ型を受け取る引数を指定、複数指定可
{
fRet = fError;
}
if(pf1 != NULL)
fclose(pf1);
if(pf2 != NULL)
fclose(pf2);
if(pf3 != NULL)
fclose(pf3);
return fRet;
}
■継承②
protected: を使うと、外部には公開されないが派生先には公開される。
オーバーライド: 派生元の関数を派生先で再定義すること
■virtual②
仮想関数にすると、派生元の関数からでも適切なオーバーライド関数を呼びだしてくれるようになる
virtualキーワードを付けることで、virtualの関数がポインタを通して呼ばれた時に、
コンパイラはそのポインタ先のオブジェクトを見てどのクラスの関数を呼ぶかを決めるようになる
詳しくは不明
■アロー演算子②
ポインタ変数からのメンバ関数呼び出し時に使用
※通常のメンバ関数の場合: t.func()
ポインタ変数のメンバ関数の場合: t->func()
int main(int argc, char* argv[])
{
Television *tvp; // テレビクラスのポインタの宣言
tvp = new Television(); // テレビクラスのオブジェクトをヒープ領域に確保
// これにより、ポインタの指す先が決まる。
// ここでコンストラクタが呼ばれる
tvp->printStatus();
tvp->setPower(1);
tvp->setChannel(8);
tvp->setVolume(10);
tvp->printStatus();
delete tvp; // new で確保したオブジェクトは delete で解放
return 0;
}
■enum(列挙型)②
列挙型とは、ざっくり言ってしまえば、「選択肢」を表す整数の定数を定義するための変数型
選択肢以外の値は入れられない 型(列挙型)の変数が定義できるようになります
enum EDirection { E_Left = 0, E_Right = 1, };
■extern②
他のモジュールで定義されている関数を参照するには、
参照元のソースでその関数がextern宣言されていなければならない。
extern int input(char *);
■static②
付加された変数または関数が静的であることを宣言
・static関数
同プロジェクト内の複数ファイルに同じ関数名,引数の
関数が存在する場合、エラーとなる。
このエラーを避けるために、どちらかの関数の前にstaticを付ける。
static int func()
すると、 int func()はそのファイル内だけのローカルな関数となり、
他のファイルの同一関数名,引数の関数とバッティングすることはなくなる。
・staticグローバル変数
同プロジェクト内の複数ファイルで同一名称のグローバル変数が
定義されていると、エラーとなる。
このエラーを避けるために、どちらかのグローバル変数の前にstaticを付ける。
static int g_var = 1;
すると、関数同様にバッティングすることはなくなる。
・staticローカル変数
ローカル変数の有効範囲を抜けても、変数の値が保持される。
何度も関数内に入ったり、ループさせた場合に変化が出てくる。
関数を抜けても情報はそのまま残るが、スコープの範囲外からは
変数を参照することはできない。
・staticメンバ変数
オブジェクトが保持するのではなく、クラスが保持することになる。
・staticメンバ関数
オブジェクトが保持するのではなく、クラスが保持することになる。
各オブジェクトが保持する通常のメンバ変数にはアクセス出来なくなる。
■operator②
「演算子オーバーロード」とは、+, -, *, / などの演算子を再実装することで、
式を楽に、しかも分かりやすく記述するための仕組み
operator+(const T&, const T&)
準備中
■auto②
変数宣言時に具体的な型名のかわりに auto キーワードを指定する事によって、
変数の型を初期化子から推論できる。
auto i = 0; //iはint型で推論
const auto l = 0L; //iはconst long型で推論
auto& r = i; //rはint&型で推論
■キャスト②
型を変換することを明示的に記載
(型名) 式
inum = (int)dnum; //dnumの値をint型に変換後、inumに代入
■sizeof②
データ型のサイズ(バイト)を求める単項演算子です。
sizeofは、原則、コンパイル時に計算されます。
sizeof (変数) //変数のバイト数を求める
・配列のサイズを調べる
using namespace std;
int main(int argc, char const* argv[])
{
int i = 0;
int a[3] = {0};
char str[] = "hoge";
cout << "i = " << sizeof (i) << endl;
cout << "a[] = " << sizeof (a) << endl;
cout << "str = " << sizeof (str) << endl;
return 0;
}
実行結果:
i = 4 //iはint型で4バイト
a[] = 12 //int型の配列が3つで4x3=12バイト
str = 5 //4文字+NULL文字文で5バイト
・配列の要素数を調べる
int main(int argc, char const* argv[])
{
int a[] = { 1,2,3 }; //int型の3要素の配列aを定義
unsigned int size = sizeof(a) / sizeof(int); //int型の3要素の配列aのバイト数12/int型4バイトで割る
cout << size << endl; //sizeは12/4で3となる。
return 0;
}
-------------------------------------------③用語-------------------------------------------
■wrap(ラッパー関数)③
ある関数を呼び出す際に、引数等の設定を行わないといけない場合に、
ラッパー関数を呼び出して、設定をしてから指定の関数を呼び出すときに
使う関数のこと。
準備中
■handle(ハンドル)③
準備中
-------------------------------------------④マクロ-------------------------------------------
■#ifdef/#endif④
機能のON/OFFに使われる
#define DEBUG
...
#ifdef DEBUG
cout << "Debug: hensuu = " << hensuu << endl;
#endif
→DEBUG というマクロが定義されていた場合に、
#ifdefと#endifの間が有効になる。
※#ifndefはDEBUGが定義されていない場合に有効になる。
■#ifndef/#endif④
二重定義防止コード
#ifndef <固有なマクロ名>
#define <固有なマクロ名>
...宣言...
#endif
#ifndef D_CommandLineArguments_H //名前はなんでもいい
#define D_CommandLineArguments_H //名前はなんでもいい
...
#endif
■マクロ④
頻出するものをマクロで1つにまとめ、さらに改造を容易にできる
マクロで数値を意味のある文字に置き換えられる。
#define <マクロ名> <差し込むテキスト>
//FUNC を void Func(int x, int y) に置き換える
#include
using namespace std;
#define FUNC void Func(int x, int y)
FUNC;
int main()
Func(1, 2);
Func(158, 144);
return 0;
}
FUNC
{
cout << "2 * " << x << " + " << y
<< " = " << 2 * x + y << endl;
}
//引数を持たせることも可能
#include
using namespace std;
#define FUNC(name) void name(int x, int y)
FUNC(Func);
int main()
{
Func(1, 2);
Func(158, 144);
return 0;
}
FUNC(Func)
{
cout << "2 * " << x << " + " << y
<< " = " << 2 * x + y << endl;
}
■定義済みマクロ④
ソース中で定義しなくても利用することができる。
例:
__DATE__ ‐ コンパイル時の日付。"Mmm dd yyyy" 形式の文字列(例、"Oct 10 2010")
__FILE__ ‐ コンパイルされるソースファイルのファイル名
__LINE__ ‐ 現在の行番号。10進数の定数で表わされる
__TIME__ ‐ ソースのコンパイル時刻。"hh:mm:ss" 形式の文字列 (例、"01:23:45")
__func__ ‐ 関数名が得られる
-------------------------------------------⑤コンテナ関連-------------------------------------------
■std::array⑤
固定長配列のコンテナクラス。
std::vector の size() や back() などの便利な機能が使えない。
それらを使いたいけど、サイズは固定でいいので vector を使うほどでもない。
ってときは std::array の出番。
std::array<型, 要素数> オブジェクト名; で宣言
std::array
・配列が空か判定する関数: empty()
if( !ar.empty() ) {
// ar が空でなければ、なんらかの処理を行う
}
■std::vector(コンテナ)⑤
コンテナ: 複数のデータを格納し、管理するオブジェクト
vector で可変長配列が使える(簡単に長さを変えられる配列を使える)
vector はクラステンプレート
#include
#include
要素指定有り(要素の初期化):
std::vector<データ型> 変数名(要素数, 初期値)
std::vector
動的配列を確保しているかどうかの判定:
empty
データ追加(末尾追加):
push_back
データ削除(末尾削除)
pop_back
int main()
{
int i;
std::vector
//std::cout << vi.size() << std::endl;
// 配列の末尾に5を追加
vi.push_back(5); //配列末尾(11番目の配列)に5を追加
for(i = vi.size(); i == vi.size(); i++)
std::cout << vi[10] << std::endl; //11番目の配列の値を出力
vi.pop_back(); //配列末尾(11番目の配列)を削除
for (i = vi.size(); i == vi.size(); i++)
std::cout << vi[10] << std::endl; //11番目の配列は削除されたのでエラーになる
return 0;
}
例:
using namespace std;
int main()
{
vector
int i; //vct(10) の (10)は指定しなくてもよい
for(i = 0; i < vct.size(); i++) //vct.sizeで指定した10分ループで"初期化"
vct[i] = 0;
for(i = 0; i < vct.size(); i++)
cout << vct[i] << ' ';
cout << endl;
vct.resize(5); //vct.resizeで配列数を変更
for(i = 0; i < vct.size(); i++)
vct[i] = 1;
for(i = 0; i < vct.size(); i++)
cout << vct[i] << ' ';
cout << endl;
return 0;
}
・vectorを連結
連結対象ベクター名.insert(連結対象ベクター開始位置, 連結する値の開始位置(ここから), 連結する値の終了位置(ここまで連結));
#include
#include
using namespace std;
int main(int argc, char const* argv[])
{
vector
v1.insert(v1.end(), v2.begin(), v2.end()); //v1の末尾からv2を全て連結
for (auto i : v1) {
cout << i << " ";
}
cout << endl;
return 0;
}
・値の参照、代入
std::vector
for (int i = 0; i < 10; ++i)
std::cout << data[i]; // data の i 番目の要素を表示
・at で参照、代入
std::vector
for (int i = 0; i < 10; ++i)
std::cout << data.at(i); // data の i 番目の要素を表示
実行結果:1 2 3 4 5 6
■std::iota⑤
連番でコンテナや配列を指定したレンジで埋める機能。
"連番"の数字でコンテナや配列を初期化する場合には、std::iotaが非常に簡単。
#include
#include
#include
using namespace std;
int main(int argc, char const* argv[])
{
vector
iota(v.begin(), v.end(), 1); //vの先頭から末尾まで、開始1から10まで順番に初期化
for (auto i : v) {
cout << i << " ";
}
cout << endl;
return 0;
}
実行結果:1 2 3 4 5 6 7 8 9 10
■イテレーター⑤
iterator: どのデータ型にも対応可能なポインタ
vectorやlistなどのコンテナオブジェクトは様々なデータ型を格納しています。
それらのデータ型にとらわれずデータを扱う方法がこのイテレータを使用した方法です。
std::vector<データ型>::iterator 変数名;
std::vector
std::vector
配列の最初を取得する:
begin
配列の最後を取得する:
end
イテレータのbeginからendまでは連続したアドレスになっているので、
beginの位置は[0]、beginの位置 + 1は[1]と考えることができます。
また、イテレータに内包されているデータにアクセスする場合は
変数の前に「*」をつけることで参照等が可能です。
++itr; で次の要素へ移動
--itr; で前の要素へ移動
vector.insert(イテレータ,値); で要素を挿入
vec.insert(vec.begin() + 1, 4);
int main() {
std::vector
std::vector
first = vi.begin();
end = vi.end();
for (itr = first; itr != end; itr++) //最初から最後まで繰り返し
{
*itr = 1000; //itrの中身に1000を代入
}
for (itr = first; itr != end; itr++)
{ std::cout << *itr << std::endl; //itrの中身を出力
}
}
autoを使うことでイテレータの長い変数名を大幅に短縮することが可能であり、
変数自体は通常のイテレータと同様に扱うことができます。
int main() {
std::vector
for (auto itr = vi.begin(); itr != vi.end(); itr++) //autoで型を推論してくれる
{
*itr = 10;
}
for (auto itr = vi.begin(); itr != vi.end(); itr++)
{
std::cout << *itr << std::endl;
}
}
任意位置挿入: 引数で指定した位置にデータを挿入する
insert
戻り値:
iterator:
挿入したデータのiterator
引数:
第一引数:
iterator:
挿入位置
第二引数:
挿入データ
std::vector
// 配列[1]に100を追加
std::vector
std::cout << *i << std::endl; //100が挿入データ
任意位置削除: 配列のデータを削除する
erase
戻り値:
削除した次の位置のiterator
引数:
その①.
iterator:
削除位置
その②.
iterator first:
削除位置の開始
iterator end:
削除位置の終端
int main() {
std::vector
vi.erase(vi.begin()); // 配列の先頭[0]を削除
vi.erase(vi.begin() + 2, vi.begin() + 5); //vi.begin()+2から5までの間を削除
for (auto itr = vi.begin(); itr != vi.end(); itr++)
{
std::cout << *itr << std::endl;
}
}
※erase()はコンテナの内容をクリアするだけで、メモリは解放されない
メモリを解放するには、 "shrink_to_fit()"を使用
vi.shrink_to_fit(); //メモリ開放
・コンテナの要素を全て削除
vi.clear();
■std::queue(キュー)⑤
FIFO(ファーストイン ファーストアウト)のキューを実現するコンテナ
std::queue<データ型> 変数名;
キューは、最初に入れられたものが最初に取り出されます。
先入れ先だしのリスト構造でデータを保持します。
取り出された値は、キューから削除されます。
キューへのデータ追加は、キューの一番後ろです。
#include
#include
using namespace std;
int
main(int argc, char const* argv[])
{
queue
q1.push (1); //pushで列の後ろにデータを追加する
q1.push (2); //pushで列の後ろにデータを追加する
q1.push (3); //pushで列の後ろにデータを追加する
cout << q1.front() << endl; //先頭のデータを取り出す キューからは削除されない
q1.pop(); //popで先頭のデータを取り出す キューから削除される
cout << q1.front() << endl;
q1.pop();
cout << q1.front() << endl;
q1.pop();
return 0;
}
実行結果:
1
2
3
キューが空かどうか確認: empty()
cout << "q1: " << q1.empty() << endl; // 0 空じゃない場合
cout << "q2: " << q2.empty() << endl; // 1 空の場合
■std::stack(スタック)⑤
LIFO(ラストイン ラストアウト)のスタックを実現するコンテナ
キューの逆で最初に入れられたものが最後に取り出される
#include
#include
int main() {
std::stack
st.push(3);
st.push(4);
std::cout << st.top() << std::endl; // 4
st.pop(); // 後に入れられた4を削除
std::cout << st.top() << std::endl; // 3
return 0;
}
実行結果:
4
3
■std::fill⑤
配列やコンテナに対して、レンジ指定をして、すべて同じ値を代入します。
std::fill(代入開始位置, 代入終了位置, 代入値);
#include
#include
//#include
using namespace std;
int main(int argc, char const* argv[])
{
vector
std::fill(v.begin(), v.end(), 3); //ベクターvの最初から最後まで3を代入
for (auto i : v) {
cout << i << " ";
}
cout << endl;
return 0;
}
■std::copy⑤
指定範囲の要素をコピーするアルゴリズムです。
std::vectorをコピーするときなどに使われます。
std::copyは、先頭から順番にコピーします。
std::reverse_copyで、コピー元と逆向きにコピーします。
std::copy(first, end, result); //firstからendまでをresultにコピー
■std::list⑤
http://vivi.dyndns.org/tech/cpp/list.html
準備中
-------------------------------------------⑥thread-------------------------------------------
■std::thread⑥
スレッドを複数作成した場合、プログラムが複数同時に実行される。
#include
#include
void worker() { //ワーカスレッド
std::cout << "thread id: " << std::this_thread::get_id() << std::endl; //get_id()でスレッドID取得
}
int main() {
std::thread th(worker); //std::thread スレッド名(動かすスレッド)でthread起動
th.join(); //スレッド名.join でthreadの終了を待つ
return 0;
}
・引数が必要な場合
std::thread th(worker, 5, 10);
・参照を渡す場合
std::thread th(worker, std::ref(value));
・threadの切り離し
実行スレッドをスレッドオブジェクトから分離して、実行を独立して続行できるようにします。
割り当てられたリソースは、スレッドが終了すると解放されます。
th.detach();
・threadがアクティブか判定
th.joinable() //アクティブならtrue/非アクティブならfalse
・別のthreadと入れ替える
int main()
{
std::thread t1([]{ /*...*/ });
std::thread t2;
t1.swap(t2); //t1スレッドとt2スレッドを入れ替える
t2.join();
return 0;
}
準備中
■std::mutex⑥
C++で同時に複数のスレッドによってアクセスされる共有データを保護するために使用
mutexを使用してロックとアンロックを行うことで、データを安全に操作できます。
準備中
-------------------------------------------⑦std(ライブラリ)関連-------------------------------------------
■アルゴリズムライブラリ⑦
https://ja.cppreference.com/w/cpp/algorithm
準備中
■boost⑦
準備中
■std::swap⑦
2つのオブジェクトの値を入れ替える、テンプレート関数
#include
#include
int main(int argc, char* argv[]) {
int x = 10, y = 20;
std::swap(x, y); //xとyの値を入れ替える
std::cout
<< "x=" << x
<< " y=" << y
<< std::endl;
return 0;
}
-------------------------------------------⑧単体テスト関連-------------------------------------------
■単体テストの仕組み⑧
#include "CppUTest/CommandLineTestRunner.h"
#include "TestTarget.h"
#include
// テストグループを定義
TEST_GROUP(TestFuncGroup)
{
//テストを実行、成功させるために必要な状態や前提条件の集合の準備
TEST_SETUP() // 各テストケースの実行直前に呼ばれる仮想メソッド
{
std::cout << " TEST_SETUP called" << std::endl;
}
TEST_TEARDOWN() // 各テストケースの実行直後に呼ばれる仮想メソッド
{
std::cout << " TEST_TEARDOWN called" << std::endl;
}
};
// テストを実行するメソッド
TEST(TestFuncGroup, TestFunc2)
{
std::cout << "TestFunc2" << std::endl;
int ret = TestFunc2(10);
CHECK_EQUAL(10, ret);
}
-------------------------------------------⑨その他-------------------------------------------
■メモリが取れなくなる⑨
int main() {
int* p = nullptr;
try {
p = new int[100000000]; //動的にメモリを確保してしまう
p = new int[100000000];
p = new int[100000000];
p = new int[100000000];
p = new int[100000000];
std::cout << p << std::endl;
}
catch (const std::bad_alloc& e) { //メモリが確保できなかった場合エラーとなる
std::cout << "cannot be allocated." << std::endl;
}
delete p;
return 0;
}