C++基礎講座5回
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
第5回はC++の動的なメモリの確保についてです
なぜ、動的なメモリ確保が必要になるのか?
そもそも動的な確保ってなにさ?って話をします。
通常、変数を宣言時
何も考えなくてもその変数の暗黙のメモリ領域が確保されます...
Visual Studio2005,2008環境下での
C++変数のint型、float型は4バイトになっています。
ある変数(関数)がどれだけのメモリ容量を確保しているかは
sizeof()演算子で調べることができます。
当然ながら、確保している変数の要領を超えた量は保存できま...
4バイトなら1バイト8ビットなので
2の32乗分の数値が判別できますが
これを超える数値は判別できず、そのような数値を代入した場合
おかしなことになります。
一番わかりやすい例は
配列の範囲外にアクセスすることです
int a[10];
とした場合, a[0]〜a[9]までの変数がつかえますが
a[10],a[11],a[-1]など配列の宣言した範囲外を使おうとした場合
メモリが確保されておらず、アクセスできずエラーになります
(仮にアクセスできたとしても関係ない値が入っている)
配列というデータ構造は
連続したアドレスにメモリを確保しています
これによりa[0]やa[1]など
高速に変数にアクセスすることが可能になっています
(連続でないデータ構造にはリストや木などがあります)
配列の長所としてはアドレスに直接アクセスできるため高速です
配列の欠点としては連続配置されているため挿入、削除をされ...
並びなおし(ソート)に時間がかかるという点です
さて、動的なメモリ確保の話にもどりますが、
プログラムの途中で使えるメモリ領域を増やしたいな
と思う場合があります
たとえば、使える配列の量を増やしたいなという場合です。
動的なメモリを確保するためには
確保したい変数型のポインタを用意します
newキーワードを使います
int型ならば
int *a = new int;
とするとint型変数のサイズ分メモリ領域が確保されます
動的に確保したメモリは必ず解放しなければなりません
解放するためには
deleteキーワードを使います
これは変数の型によらず
delete a;//←aは任意の変数型のポインタ
で解放できます
配列でも動的なメモリは確保できます
int型のデータを100個もつ配列を作りたい場合は
int *a = new int[100];
配列の場合の解放は
delete[] a;//←aは任意の変数型のポインタ
で行います
使い終わったら必ずnewしたメモリはdelete、delete[]で解放を...
これを行わないとプログラム終了時にメモリが解放されず
プログラムを起動ごとに使えるメモリ領域が減っていきます
(メモリリークという)
特に携帯電話などメモリ使用が限られている環境でこれをやる...
プログラムを起動し続けるとメモリ容量がなくなり、プログラ...
メモリは資源だという認識を常に頭にいれておいてください
ここではとりあえず、配列の動的確保について説明します
単体でメモリ確保していくのはリストや木などのデータ構造に...
それについては俺の余力があったらいつか説明します
#include <iostream>
using namespace std;
int main()
{
// int型ポインタ
int *memory;
// int型を配列で100個分確保
memory = new int[100];
for(int i = 0;i < 100;++i)
{
memory[i] = i;
cout << memory[i] << endl;
}
// 使わなくなったり、再確保したかったりする場合は...
delete[] memory;
// int型を配列で200個分確保
memory = new int[200];
for(int i = 0;i < 200;++i)
{
memory[i] = i;
cout << memory[i] << endl;
}
// 使わなくなったり、再確保したかったりする場合は...
delete[] memory;
return 0;
}
newキーワードの利点はC言語でのmalloc関数と違い
メモリ領域確保のためのデータ型のサイズをいちいち調べなく...
また、malloc関数で確保するときはクラスのコンストラクタは...
よってC++では(クラスを使うので)newでメモリの動的確保...
(お兄さんとの約束だぞ☆)
C++にはこれとは別にvectorという可変長配列のテンプレー...
がありますが、配列の動的確保はファイル読み込みとかで
必要なデータ数がファイルによって異なる場合とか重宝します
さて、ここで
メモリ領域を途中で増設したいなと思うときがあります。
しかもよく考えるのが配列みたいに連続で確保できないかなぁ...
みたいなことです。
連続で確保したい理由としては、
配列の利点である添え字で1発でアクセスできるという利点を生...
(for文などで繰り返し処理させたいなぁと思うときもあるでし...
C言語ではrealloc関数という便利な関数で
現状の動的メモリ領域のデータを維持したまま、連続でメモリ...
しかし、C++言語にはrenewなどというキーワードや関数は存...
ではどうするかというと、
現状の動的メモリ配列と別に拡張後の動的メモリ配列を用意し
memcpy関数で移植します。
int *data = new int[100];
//なんかdataに適当なデータをいれる処理後
// dataと別に動的配列を確保
int *temp = new int[200];
memcpy(temp,data,sizeof(int) * 100);
// コピーし終わったら元の動的配列はいらない
delete[] data;
// 終了処理
delete[] temp;
はい、非常にめんどくさいですね(・ω・`)
それにどの程度の量のメモリを再確保しなければならないのか
わからない場合というのが多々あります
ここで可変長配列クラスというものがあります
vectorクラスと呼ばれるもので
(追記:やってることは上記のようなメモリ再確保を内部で自動...
どんな型でもテンプレートと呼ばれる機能により格納できる可...
スタックやキューみたいに逐次データを連続で詰めていくこと...
使い方は次のソースコードに示します
vectorヘッダーをインクルードします
#include <iostream>
#include <vector>
using namespace std;
class Myclass
{
public:
int number;
};
int main()
{
// int型の可変長配列の宣言
vector<int> ArrayInt;
// 可変長配列にデータを代入
for(int i = 0;i < 100;++i)
{
// 配列の後ろの要素を生成し、int型データを詰めていく
ArrayInt.push_back(i);
}
// 生成後の可変長配列の使い方は普通の配列と同じ
// ArrayInt.size()で可変長配列の要素数がわかる
for(unsigned int i = 0;i < ArrayInt.size();++i)
{
cout << ArrayInt[i] << endl;
}
cout << endl;
// Myclass型の可変長配列の宣言
vector<Myclass> ArrayMyclass;
// 可変長配列にデータを代入
for(unsigned int i = 0;i < 100;++i)
{
Myclass temp;
temp.number = i * 2;
// 配列の後ろの要素を生成し、Myclass型データを詰めていく
ArrayMyclass.push_back(temp);
}
for(unsigned int i = 0;i < 100;++i)
{
cout << ArrayMyclass[i].number << endl;
}
return 0;
}
なお、最初から配列の個数が分かっている場合は
// int型の可変長配列の宣言時に100個用意する
vector<int> ArrayInt(100);
ArrayInt[99] = 10;
もしくはresizeメソッドを使い
// int型の可変長配列の宣言
vector<int> ArrayInt;
// resizeメソッドで100個確保
ArrayInt.resize(100);
ArrayInt[99] = 10;
のようにやればnew[]の配列みたいに動的確保することができます
さらにvectorは自動的に破棄してくれるので
delete[]をする必要性もありません
若干高等なお話:
pushbackで配列に詰める前に
要素数がある程度わかっている場合は
reserveメソッドで使う個数を予約するといいです(この時点で...
というのはvectorクラスはpushbackするたびにあらかじめ用意...
内部的にnew,deleteを行っているからです
頻繁にnewするとメモリに隙間が空いてしまう(断片化してしま...
(newでメモリを確保するという処理自体も遅い)
使う個数があらかじめ何個以上とわかっている場合は
内部的なメモリ確保の回数を減らすことが大事です
使い方は100個以上使うことがあらかじめわかっている場合
pushbackの前に
ArrayInt.reserve(100);
とします。
おまけ:
C++、C言語でforループなどで
配列の要素数を知りたいと思うことが多々あると思います
そんな場合は次のようなマクロ関数(簡易関数)を
プログラムの先頭に書くのをおススメします
#define LENGTH(p) (sizeof(p))/(sizeof(p[0])) //� �配...
#define LENGTHMEM(p) (_msize(p)) / (sizeof(p[0])) //�◆�...
使い方はfor文などで
// 通常の配列
int a[10];
for(unsigned int i = 0;i < LENGTH(a);++i)
{
// 処理
}
// 動的確保した配列
int *b = new int[20];
for(unsigned int i = 0;i < LENGTHMEM(b);++i)
{
// 処理
}
delete[] b;
とすれば、マクロ関数で要素数がわかるようになっています
(注意:_msizeは動的なクラス配列には使えません)
さらに、おまけ
メモリリークの検出方法
知らない人は必須の情報
メモリリーク(動的確保して解放してないメモリ)を検出する...
まず、
#include <crtdbg.h> // メモリリーク検出
をインクルードします、このヘッダー<crtdbg.h>は
デバックモードでしか使えません
デバックモードは実行ファイル出力のビルド形式をDebugの状態...
#ifdef _DEBUG
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _C...
#endif
ここで
#ifdef _DEBUG
#endif
で囲まれた部分は実行ファイル出力のビルド形式がDebug状態で...
つまりReleaseモードなどでは素通りします
DebugモードでF5キーで実行し終了すると
解放されてないメモリのアドレスとバイト数とバイト情報が出...
追記:vectorクラスに代表されるSTLはDebugモードだとエラー...
完成版を実行ファイルとして出すときは必ずReleaseモー...
|(^ω^)わかったお|70|
|普通|5|
|。(`ω´#)。わかりにくぃぜぇえええええ|2|
終了行:
第5回はC++の動的なメモリの確保についてです
なぜ、動的なメモリ確保が必要になるのか?
そもそも動的な確保ってなにさ?って話をします。
通常、変数を宣言時
何も考えなくてもその変数の暗黙のメモリ領域が確保されます...
Visual Studio2005,2008環境下での
C++変数のint型、float型は4バイトになっています。
ある変数(関数)がどれだけのメモリ容量を確保しているかは
sizeof()演算子で調べることができます。
当然ながら、確保している変数の要領を超えた量は保存できま...
4バイトなら1バイト8ビットなので
2の32乗分の数値が判別できますが
これを超える数値は判別できず、そのような数値を代入した場合
おかしなことになります。
一番わかりやすい例は
配列の範囲外にアクセスすることです
int a[10];
とした場合, a[0]〜a[9]までの変数がつかえますが
a[10],a[11],a[-1]など配列の宣言した範囲外を使おうとした場合
メモリが確保されておらず、アクセスできずエラーになります
(仮にアクセスできたとしても関係ない値が入っている)
配列というデータ構造は
連続したアドレスにメモリを確保しています
これによりa[0]やa[1]など
高速に変数にアクセスすることが可能になっています
(連続でないデータ構造にはリストや木などがあります)
配列の長所としてはアドレスに直接アクセスできるため高速です
配列の欠点としては連続配置されているため挿入、削除をされ...
並びなおし(ソート)に時間がかかるという点です
さて、動的なメモリ確保の話にもどりますが、
プログラムの途中で使えるメモリ領域を増やしたいな
と思う場合があります
たとえば、使える配列の量を増やしたいなという場合です。
動的なメモリを確保するためには
確保したい変数型のポインタを用意します
newキーワードを使います
int型ならば
int *a = new int;
とするとint型変数のサイズ分メモリ領域が確保されます
動的に確保したメモリは必ず解放しなければなりません
解放するためには
deleteキーワードを使います
これは変数の型によらず
delete a;//←aは任意の変数型のポインタ
で解放できます
配列でも動的なメモリは確保できます
int型のデータを100個もつ配列を作りたい場合は
int *a = new int[100];
配列の場合の解放は
delete[] a;//←aは任意の変数型のポインタ
で行います
使い終わったら必ずnewしたメモリはdelete、delete[]で解放を...
これを行わないとプログラム終了時にメモリが解放されず
プログラムを起動ごとに使えるメモリ領域が減っていきます
(メモリリークという)
特に携帯電話などメモリ使用が限られている環境でこれをやる...
プログラムを起動し続けるとメモリ容量がなくなり、プログラ...
メモリは資源だという認識を常に頭にいれておいてください
ここではとりあえず、配列の動的確保について説明します
単体でメモリ確保していくのはリストや木などのデータ構造に...
それについては俺の余力があったらいつか説明します
#include <iostream>
using namespace std;
int main()
{
// int型ポインタ
int *memory;
// int型を配列で100個分確保
memory = new int[100];
for(int i = 0;i < 100;++i)
{
memory[i] = i;
cout << memory[i] << endl;
}
// 使わなくなったり、再確保したかったりする場合は...
delete[] memory;
// int型を配列で200個分確保
memory = new int[200];
for(int i = 0;i < 200;++i)
{
memory[i] = i;
cout << memory[i] << endl;
}
// 使わなくなったり、再確保したかったりする場合は...
delete[] memory;
return 0;
}
newキーワードの利点はC言語でのmalloc関数と違い
メモリ領域確保のためのデータ型のサイズをいちいち調べなく...
また、malloc関数で確保するときはクラスのコンストラクタは...
よってC++では(クラスを使うので)newでメモリの動的確保...
(お兄さんとの約束だぞ☆)
C++にはこれとは別にvectorという可変長配列のテンプレー...
がありますが、配列の動的確保はファイル読み込みとかで
必要なデータ数がファイルによって異なる場合とか重宝します
さて、ここで
メモリ領域を途中で増設したいなと思うときがあります。
しかもよく考えるのが配列みたいに連続で確保できないかなぁ...
みたいなことです。
連続で確保したい理由としては、
配列の利点である添え字で1発でアクセスできるという利点を生...
(for文などで繰り返し処理させたいなぁと思うときもあるでし...
C言語ではrealloc関数という便利な関数で
現状の動的メモリ領域のデータを維持したまま、連続でメモリ...
しかし、C++言語にはrenewなどというキーワードや関数は存...
ではどうするかというと、
現状の動的メモリ配列と別に拡張後の動的メモリ配列を用意し
memcpy関数で移植します。
int *data = new int[100];
//なんかdataに適当なデータをいれる処理後
// dataと別に動的配列を確保
int *temp = new int[200];
memcpy(temp,data,sizeof(int) * 100);
// コピーし終わったら元の動的配列はいらない
delete[] data;
// 終了処理
delete[] temp;
はい、非常にめんどくさいですね(・ω・`)
それにどの程度の量のメモリを再確保しなければならないのか
わからない場合というのが多々あります
ここで可変長配列クラスというものがあります
vectorクラスと呼ばれるもので
(追記:やってることは上記のようなメモリ再確保を内部で自動...
どんな型でもテンプレートと呼ばれる機能により格納できる可...
スタックやキューみたいに逐次データを連続で詰めていくこと...
使い方は次のソースコードに示します
vectorヘッダーをインクルードします
#include <iostream>
#include <vector>
using namespace std;
class Myclass
{
public:
int number;
};
int main()
{
// int型の可変長配列の宣言
vector<int> ArrayInt;
// 可変長配列にデータを代入
for(int i = 0;i < 100;++i)
{
// 配列の後ろの要素を生成し、int型データを詰めていく
ArrayInt.push_back(i);
}
// 生成後の可変長配列の使い方は普通の配列と同じ
// ArrayInt.size()で可変長配列の要素数がわかる
for(unsigned int i = 0;i < ArrayInt.size();++i)
{
cout << ArrayInt[i] << endl;
}
cout << endl;
// Myclass型の可変長配列の宣言
vector<Myclass> ArrayMyclass;
// 可変長配列にデータを代入
for(unsigned int i = 0;i < 100;++i)
{
Myclass temp;
temp.number = i * 2;
// 配列の後ろの要素を生成し、Myclass型データを詰めていく
ArrayMyclass.push_back(temp);
}
for(unsigned int i = 0;i < 100;++i)
{
cout << ArrayMyclass[i].number << endl;
}
return 0;
}
なお、最初から配列の個数が分かっている場合は
// int型の可変長配列の宣言時に100個用意する
vector<int> ArrayInt(100);
ArrayInt[99] = 10;
もしくはresizeメソッドを使い
// int型の可変長配列の宣言
vector<int> ArrayInt;
// resizeメソッドで100個確保
ArrayInt.resize(100);
ArrayInt[99] = 10;
のようにやればnew[]の配列みたいに動的確保することができます
さらにvectorは自動的に破棄してくれるので
delete[]をする必要性もありません
若干高等なお話:
pushbackで配列に詰める前に
要素数がある程度わかっている場合は
reserveメソッドで使う個数を予約するといいです(この時点で...
というのはvectorクラスはpushbackするたびにあらかじめ用意...
内部的にnew,deleteを行っているからです
頻繁にnewするとメモリに隙間が空いてしまう(断片化してしま...
(newでメモリを確保するという処理自体も遅い)
使う個数があらかじめ何個以上とわかっている場合は
内部的なメモリ確保の回数を減らすことが大事です
使い方は100個以上使うことがあらかじめわかっている場合
pushbackの前に
ArrayInt.reserve(100);
とします。
おまけ:
C++、C言語でforループなどで
配列の要素数を知りたいと思うことが多々あると思います
そんな場合は次のようなマクロ関数(簡易関数)を
プログラムの先頭に書くのをおススメします
#define LENGTH(p) (sizeof(p))/(sizeof(p[0])) //� �配...
#define LENGTHMEM(p) (_msize(p)) / (sizeof(p[0])) //�◆�...
使い方はfor文などで
// 通常の配列
int a[10];
for(unsigned int i = 0;i < LENGTH(a);++i)
{
// 処理
}
// 動的確保した配列
int *b = new int[20];
for(unsigned int i = 0;i < LENGTHMEM(b);++i)
{
// 処理
}
delete[] b;
とすれば、マクロ関数で要素数がわかるようになっています
(注意:_msizeは動的なクラス配列には使えません)
さらに、おまけ
メモリリークの検出方法
知らない人は必須の情報
メモリリーク(動的確保して解放してないメモリ)を検出する...
まず、
#include <crtdbg.h> // メモリリーク検出
をインクルードします、このヘッダー<crtdbg.h>は
デバックモードでしか使えません
デバックモードは実行ファイル出力のビルド形式をDebugの状態...
#ifdef _DEBUG
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _C...
#endif
ここで
#ifdef _DEBUG
#endif
で囲まれた部分は実行ファイル出力のビルド形式がDebug状態で...
つまりReleaseモードなどでは素通りします
DebugモードでF5キーで実行し終了すると
解放されてないメモリのアドレスとバイト数とバイト情報が出...
追記:vectorクラスに代表されるSTLはDebugモードだとエラー...
完成版を実行ファイルとして出すときは必ずReleaseモー...
|(^ω^)わかったお|70|
|普通|5|
|。(`ω´#)。わかりにくぃぜぇえええええ|2|
ページ名: