お世話になります。
現在、Visual Studio 2005にてMFCのC++のプログラムを作成しているのですが、そこでメモリの使用容量について疑問に感じた点があったのでこちらで、質問させて頂きました。

お聞きしたい内容は
あるクラス1内で別のクラス2を宣言する際に、メンバ変数としてそのクラス2を宣言し、クラス1のコンストラクタでnewでメモリを確保し、デスクリタでdeleteする方法と、その都度クラス2の変数または関数が必要なときにnewでメモリを確保して、deleteで開放する方法とでは、メモリの確保等で違いがなにかありますでしょうか?
また、クラス2をクラス1,クラス3で使用する場合には、クラス1,クラス3でそれぞれクラス2のオブジェクトを宣言するのと、クラス1でクラス2のオブジェクトを宣言し、そのオブジェクトをクラス3でexternするのではどちらの方がメモリの使用等からよい方法なのでしょうか?
今までほとんどメモリを気にせずにプログラムを作っていた為、メモリの使用の点ではほとんど無知な為、変な質問なのかもしれませんが、ご存知の方がいらっしゃいましたら、ご回答をお願い致します。

開発環境は
Widows CE 6.0
Visual Studio 2005
です。

A 回答 (3件)

その都度、オブジェクトを生成して済むのであれば、静的メンバ関数にできないか検討してみてはどうでしょうか?


静的メンバ関数にできるのであれば、オブジェクトを生成する必要がなくなりますから、メモリの使用量はゼロになります。
    • good
    • 0
この回答へのお礼

その都度クラス1,クラス3で呼ばれるクラス2の関数を静的関数にしてみたらいい、ということでしょうか?
そうすれば、確かにメモリの使用はなくなりますよね。
その方法で考えてみます!!

お礼日時:2009/05/25 18:26

>どちらの方がメモリの使用等からよい方法なのでしょうか?



"等"という言葉があったのでツッコミを入れさせていただきます。
メモリの使用だけに限定されないと解釈しています。

まず、何度も何度もnewを繰り返すと”メモリの断片化(フラグメント)"が
起きる恐れがあります。

http://yougo.ascii.jp/caltar/%E3%83%95%E3%83%A9% …

最近のPCであればメモリを十分に積んでいるので
1) 1MByte new
2) 1MByte delete
3) 100kByte new
4) 100kByte delete
5)1MByte new
6)1Mbyte delete
をするよりは
最初から1MByteをnewもしくはstaticで確保して、使いまわすほうが
好ましいと思われます。
もちろん3)と4)の間では900kByteも無駄が生じることは
百も承知ですが
2GByteの物理メモリで900kByte気にしてもしゃーない
という”富豪プログラミング"です。

あと
>クラス1のコンストラクタでnewでメモリを確保し、デスクリタでdeleteする方法

この場合、クラス1にクラス2の"ポインタ"をもつということになりますね

>クラス1,クラス3でそれぞれクラス2のオブジェクトを宣言する
この場合、クラス2のオブジェクトをクラス1(および3)が抱え込む形ですね。

この2つのパターンにはメモリ以外に重要な違いがあります。
それは前者(ポインタ)の場合は"前方宣言"で済ますことが可能ということです。

前方宣言を使うと、クラス1のコンパイルにクラス2の情報は不要になります。
クラス2の実装を変更してもクラス1はコンパイルしなおさなくて済みます。
ことは、ビルド時間の長短の問題だけではありません。

クラス2はクラス1に影響を与えず、まったく自由に変更できるということは
クラス2はそれと機能面で互換性のある2aや2b...と自由に交換可能に
なるのです。
これは、複雑なプログラムを組む時に非常に大きなメリットとなります。

以上、メモリの瞬間・瞬間の利用効率以外の
長期的視点からみてみました。

以上の視点とメモリを極力無駄にしない努力を
時には天秤にかけ
どちらを優先するか判断してみてください。
    • good
    • 0
この回答へのお礼

丁寧なご回答ありがとうございます。
メモリのnewとdeleteを繰り返すと、フラグメントが発生するのですか。。それは初めて聞きました、参考にさせて頂きます。
ということは、その都度のメモリの使用のみを考えて、あるクラスの各関数内で、ローカル変数として、別のクラスのオブジェクトのnew.deleteを繰り返してメモリを確保するよりも、長期的にみてあるクラスのグローバル変数として他のクラスのオブジェクトを宣言し、コンストラクタでnew,デストラクタでdelteした方がよいとも考えられるということですね。

また、教えて頂いたクラスのオブジェクトの前者、後者の違いですが、あるクラスで別のクラスのオブジェクトを宣言するときは、ポインタとして呼び出す方が、利便性があるということですね。

ご回答ありがとうございます。

お礼日時:2009/05/27 09:00

クラスのインスタンスがメモリー上に実在するのは


クラスのコンストラクタが実行されてインスタンス
が生成されてから、そのインスタンスがデストラクタで
破棄されるまでで、これをクラスの寿命といいます。
つまり生きているクラスは全てメモリ上に存在するわけです。

以上の理屈より、ご質問の件は自明ですね(^^)/。
    • good
    • 0
この回答へのお礼

ということはクラス1のメンバ変数にクラス2のインスタンスを指定し、クラス1のコンストラクタでnewでメモリを確保すると、クラス1のデストラクタが呼ばれるまで常にクラス2のインスタンス分のメモリが確保され続けてしまうということですね?
逆に、その都度new,deleteを呼ぶことでインスタンス分のメモリの確保・開放を繰り返しているということですか。

だとしたら、面倒でも何度も繰り返しインスタンスを作成したほうがメモリの面ではいいということですね。

お礼日時:2009/05/25 18:23

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QLNK2019: 未解決の外部シンボルのエラーが出る

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自キャラのデータ
Point2D g_jikipos = {40, 400};//自キャラの座標

//画像ハンドル
int g_jikiimage[11];

//色々なファイルの読み込み
int LoadFiles(){
//画像ファイル読み込み
if(LoadDivGraph("media\\player01.bmp",
11,11,1,64,64,g_jikiimage) == -1) return -1;

return 1;
}


 mymain.h
//他から呼び出させるMyMainの関数
void MyMain();
int LoadFiles();


 myhelper.h(サンプルなので打ちミスはない)
#include "DxLib.h"
#include <limits.h>
#include <math.h>

//構造体宣言
//座標またはベクトルを記録する構造体
struct Vector{
float x,y;
};
typedef Vector Point2D;
//線を記録する構造体
struct Line2D{
Point2D startpos, endpos;
float katamuki;//傾きをラジアン値で記録
Vector speed;//移動している場合は速度をセット
};
//球体を記録する構造体
struct Ball2D{
Point2D position;
float hankei;//半径
};
//四角形を記録する構造体
struct Rect2D{
Point2D lefttop;
Point2D rightbottom;
float width;
float height;
};


//ライブラリ関数
Point2D PosInView(Point2D in);
int XInView(float inx);
int YInView(float iny);
void ScrollToLeft(float jikiposx);
void ScrollToRight(float jikiposx);
void ScrollToUp(float jikiposy);
void ScrollToDown(float jikiposy);
void DrawLineInView(float x1, float y1, float x2, float y2, int Color, int Thickness);
void DrawCircleInView(float x, float y, float r, int Color, int FillFlag);
void DrawAnimation(float x, float y, double ExtRate, double Angle,int TurnFlag,
int *imgarray, int allframe, float fps);
//ベクトル関数
Vector CreateVector(Vector in, float veclen);
Vector AddVector(Vector v1, Vector v2);
Vector SubVector(Vector v1, Vector v2);
Vector AddVectorInFrameTime(Vector pos, Vector speed);
Vector AddVectorInFrameTime2(Vector pos, Vector speed, Vector accel);
Vector Normalize(Vector in);
Vector RotateVector(Vector in, float radian);
float VectorLengthSquare(Vector in);
float DotProduct(Vector v1, Vector v2);
float CrossProduct(Vector v1, Vector v2);
void SetLine2DKatamuki(Line2D *in);
void DrawLine2D(Line2D in, int Color, int Thickness);
void DrawBall2D(Ball2D in, int Color, int Fill);
//当たり判定関数
bool HitTestLineAndBall(Line2D linein, Ball2D ballin);
bool IsPointAtLineFace(Line2D linein, Point2D ptin);
bool HitTestLineAndLine(Line2D line1, Line2D line2);
bool HitTestBallAndBall(Ball2D a, Ball2D b);
bool HitTestPointAndBox(Rect2D rect, Point2D pt);
//タイマー関数
void SetSimpleTimer(int idx, int time);
int GetPassedTime(int idx);


//グローバル変数
extern float g_frametime;
extern Rect2D g_framerect;//画面領域(当たり判定)
extern Point2D g_current_field_pos;//現在の左上座標
extern Rect2D g_stagesize;//ステージサイズ

//定数宣言
const float ZEROVALUE = 1e-10f;
const float PIE = 3.1415926f;
const int SCROLL_LIMIT = 200;
----------------------------------------------------------------
 エラー内容
1>myhelper.obj : error LNK2019: 未解決の外部シンボル "void __cdecl MyMain(void)" (?MyMain@@YAXXZ) が関数 _WinMain@16 で参照されました
1>C:\Documents and Settings\Owner\My Documents\Visual Studio 2008\Projects\my\Debug\my.exe : fatal error LNK1120: 外部参照 1 が未解決です
1>my - エラー 2、警告 0
ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ
----------------------------------------------------------------
画像を貼り付けときます
(見えにくい場合→http://www.dotup.org/uploda/www.dotup.org154142.jpg.html)
初心者なのでわかりやすくお願いします

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自...続きを読む

Aベストアンサー

ファイル構成から推測するに
mymain.cpp というファイルに
void MyMain(void) {
// ここに処理を書く
}
という関数が必要なようです。

Qファイルやディレクトリの存在確認を行う方法

ファイルをオープンするのはfopenでOKですが、ファイルやディレクトリの存在確認を行う方法が知りたいです。

何か組み合わせて作るものなのでしょうか?
perlとか便利な演算子があるのですが、C/C++って器用ではないですね。
これは処理系?依存の内容ですか?

私の環境は VC6, VC2005 Windows2000です。

Aベストアンサー

int access(const char* path, int mode);
int stat(const char* path, struct stat* sb);

かな?
MSDN を引くと _access_s() を使えとか書いてあるけど。

QDWORDの実際の型は何でしょうか

VC++.NETの環境です。
DOWRD dw1 = 1;
int i = 2; と定義し
ここで
if ( i > dw1 ){
何かの処理;
}
とコーディングすると
warning C4018: '>' : signed と unsigned の数値を比較しようとしました。
のワーニングがでます。
これは、DWORDがint型でなくunsigned int型のようにも見えます。
ある本によれば(VC++.V.NET逆引き大全500の極意)
DWORD はint型であると記述されています。
もし、int型ならこのワーニングはでないはずなのですが、
なぜでるのでしょうか。又、DWORDの実際の型は何なのでしょうか。ご存じのかたおりましたら、教えていただけませんでしょうか。

Aベストアンサー

型定義が知りたいのならば、宣言ファイルを見れば疑問を挟む余地もありません。
DWORD型はwindef.hで
"typedef unsigned long DWORD;"
と宣言されています。

Visual Studioを使っているのならば、知りたい型の上にマウスポインタを置いて右クリック、ポップアップメニューの「定義へ移動」または「宣言へ移動」で簡単に知ることが出来ます。

Qエクセルで、条件に一致した行を別のセルに抜き出す方法

エクセルで、指定した条件に一致するセルを含む行をすべて抜き出す方法が知りたいです。

たとえば、

<A列> <B列> <C列>
7/1 りんご 100円
7/2 ぶどう 200円
7/2 すいか 300円
7/3 みかん 100円

このような表があって、100円を含む行をそのままの形で、
別のセル(同じシート内)に抜き出したいのですが。

7/1 りんご 100円
7/3 みかん 100円

抽出するだけならオートフィルターでもできますが、
抽出結果を自動的に、別の場所に、常に表示させておきたいのです。

初歩的な質問だと思いますが、検索しても分からなかったので、よろしくお願いします。

Aベストアンサー

同じ質問が結構よく出てますが、そんなに初歩的でもありません
別シートのA1セルに「100円」と入力し、そのシートの任意のセルに以下の式を貼り付けて下さい。後は、下方向、右方向にコピー。
日付のセル書式は「日付」形式に再設定してください

=IF(COUNTIF(Sheet1!$C:$C,$A$1)>=ROW(A1),INDEX(Sheet1!A:A,LARGE(INDEX((Sheet1!$C$1:$C$500=$A$1)*ROW(Sheet1!$C$1:$C$500),),COUNTIF(Sheet1!$C:$C,$A$1)-ROW(A1)+1)),"")

データ範囲は500行までとしていますが、必要に応じて変更して下さい

Qenumの値から定義名を文字列化したい

C++でenumの値を元に、定義した名前(Fruits_Appleとか)を文字列として取得したいのですが、何か良い方法はないでしょうか?
switch文等で分岐させて...という方法もありますが、enumの定義数が多くなるとコードが複雑になるので、シンプルな方法をさがしています。


enum Fruits{
Fruits_Apple,
Fruits_Orange,

Fruits_End
};

void printFruits(Fruits fruits)
{
// ここで引数fruitsの値に応じて"Fruits_Apple"とか"Fruits_Orange"とかを
// 文字列に代入したい
std::string fruitsName = "Fruits_Apple";
}

Aベストアンサー

 こんばんは。
 例えば、boostライブラリのメタプリプロセッサの如く、可視性など関係なしに手段を選ばなければナンボでも出来ます。
 ただし一回でも陥ると地獄そのものです。

//-------------------------------------------------------------------------------------
//misc.hpp
//-------------------------------------------------------------------------------------
#ifndef __MISC_HPP__
#define __MISC_HPP__
#define ArrayCount(a) (sizeof(a)/sizeof(a[0]))
#define ArrayEnd(a) (a + ArrayCount(a))
#endif
//-------------------------------------------------------------------------------------
//misc.hpp
//-------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------
//fruits.hpp
//-------------------------------------------------------------------------------------
#include"misc.hpp"

//enumを作成する
#ifdef MAKEENUM

#define MAKEHDR(name) enum name {
#define MAKELINE(name) name
#define MAKEFTR(name) };

//arr##name##Pair[]を作成して、map##name##Resultに入れる
#else

#define MAKEHDR(name)std::pair<name, std::string> arr##name##Pair[] = {
#define MAKELINE(name)std::make_pair(name, std::string(#name))
#define MAKEFTR(name)}; \
std::map<name, std::string> map##name##Result;\
{for(size_t n = 0; n < ArrayCount(arr##name##Pair); ++n)map##name##Result.insert(arr##name##Pair[n]);}

#endif

MAKEHDR(Fruits)
MAKELINE(Fruits_Apple),
MAKELINE(Fruits_Orange),
MAKELINE(Fruits_End)
MAKEFTR(Fruits)

MAKEHDR(Gender)
MAKELINE(Gender_Male),
MAKELINE(Gender_Female),
MAKELINE(Gender_End)
MAKEFTR(Gender)

#undef MAKEHDR
#undef MAKELINE
#undef MAKEFTR
#undef MAKEENUM
//-------------------------------------------------------------------------------------
//fruits.hpp
//-------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------
//test.cpp
//-------------------------------------------------------------------------------------
#include<map>
#include<string>
#include<iostream>

//enumの作成
#define MAKEENUM
#include"fruits.hpp"

static void Print(Fruits fruits, Gender gender)
{
//map##name##Resultの作成
#include"fruits.hpp"

std::cout << mapFruitsResult[fruits] << std::endl;
std::cout << mapGenderResult[gender] << std::endl;
}

int main()
{
::Print(Fruits_Apple, Gender_Female);
return 0;
}
//-------------------------------------------------------------------------------------
//test.cpp
//-------------------------------------------------------------------------------------

 こんばんは。
 例えば、boostライブラリのメタプリプロセッサの如く、可視性など関係なしに手段を選ばなければナンボでも出来ます。
 ただし一回でも陥ると地獄そのものです。

//-------------------------------------------------------------------------------------
//misc.hpp
//-------------------------------------------------------------------------------------
#ifndef __MISC_HPP__
#define __MISC_HPP__
#define ArrayCount(a) (sizeof(a)/sizeof(a[0]))
#define ArrayEnd(a) (a...続きを読む

Q配列の要素数に変数を入れたいときには

よろしくお願いします。
配列の要素数には定数しか入れられないのですが,どうしても変数を入れたいときは,それを引数として関数を呼び出すしか方法はないでしょうか。
具体的には,scanfで手に入れたint型の変数を要素数とする配列を宣言したいのですが,どうすれば良いでしょうか。
ご教授ください。

Aベストアンサー

c99と呼ばれる最近の規格では、配列の大きさに変数を使用できます。
bccはc99に対応していないようです。

それ以前の規格では、動的領域確保関数 malloc や callocを使って領域を確保するか、効率等を無視してバカデカい配列を用意しておくかです。
「それを引数として関数を呼び出す」っていうのは、malloc/callocのことですか?

QC++言語で、構造体のコピーは可能(しても良い)のでしょうか?

C++言語で、構造体のコピーは可能(しても良い)のでしょうか?
問題がある場合は、なぜだめなのか知りたいです。
構造体は可変長ではありません。

typedef struct kumi {
char namae[10];
int ten;
}Kumi;

Kumi a, b;

strcpy(a.namae, "AAA");
a.ten = 50;

b = a;

Aベストアンサー

「C++」では何の問題もありません.
「C」だと, ふる~い時代の遺跡級のコンパイラが文句を言うかもしれません. ま, その手の遺跡級のコンパイラだと
・void がない (void * は char * で代用する)
・プロトタイプが存在しない
・const や volatile がない
・ひょっとすると単項の + もない
など, 今の視点からするといいたいことは山ほどあります (さすがに a -= b; ではなく a =- b; とするコンパイラを使うことはないだろう).
そのような時代には構造体のコピーを memcpy でやっていましたが, C++ では逆に危険です.

Qメンバ変数のサイズの増加

  こんにちは。
 C++でクラスの大きさが重要になり、クラスのサイズを軽減しようとしていたんですが、メンバ変数
でbool型の変数を一つ追加しただけで 8byte増加してしまいました。
 何故、bool型なのに8byteも増加してしまったのかわかりません。
そのクラスには仮想関数が二つあります。
 それが原因で仮想関数が増すごとにクラスのサイズが増加するのは分かりますが、メンバ変数を一つ追加しただけで8byteも増加するのが理解できません。 その後bool型を追加したんですけどサイズは増えませんでした。
 bool型をそのまま増やして9個目からまた8byte増えるようです。
 何故1byteづつ増えていかないんでしょうか? よろしくお願いします。

Aベストアンサー

下記【1】に簡単な説明がありますが、さらにかみ砕いて言うと、プロセッサによるデータ読み出しを高速化するために、大抵のコンパイラでは、構造体の全体のサイズがレジスタサイズの整数倍になるように、詰め物(パディング)が勝手に挿入されることが多いです。

【1】
http://www.office-matsunaga.biz/evctips/evctips01.html

ちなみにVisual C++ 2010の既定のコンパイル オプションでテストしたら下記のようになります。
とりあえずclassの代わりにstructを使っていますが、基本的にどちらも一緒です。


struct Test1
{
};

struct Test2
{
bool a;
};

struct Test3
{
bool a;
bool b;
};

struct Test4
{
int x;
bool a;
};

#pragma pack(1)
struct Test5
{
int x;
bool a;
};
#pragma pack()

struct Test6
{
virtual ~Test6() {}
};

sizeof(Test1) == 1;
sizeof(Test2) == 1;
sizeof(Test3) == 2;
sizeof(Test4) == 8;
sizeof(Test5) == 5;
sizeof(Test6) == 4; // Win32(x86)
sizeof(Test6) == 8; // x64

Test6構造体は仮想関数テーブルへのポインタを内部的に持っているため、x86とx64でサイズが異なります。

特定の構造体のアライメントを調整するには、Test5構造体のように(処理系依存の)#pragma packを使うことができます。
すべての構造体のアライメントを一括調整するには、コンパイル オプション(/Zp1~16)を使います。
(IDEから設定する場合、プロジェクト プロパティの「C/C++ ==> コード生成 ==> 構造体メンバーのアライメント」。VC 2003とVC 2010では若干違うかも)

ただ、よっぽどのことがないかぎり、(アクセス効率が落ちるので)既定のアライメントを変更しないほうがいいです。
ファイルへのシリアライズやファイルからの逆シリアライズをする場合、パディングを取り除く目的でアライメント調整をするときには、処理系依存を覚悟で調整することもあります。が、仮想関数を持つ=仮想関数テーブルへのポインタを持つクラスに対してアライメント調整を行なうことはまずないかと。

下記【1】に簡単な説明がありますが、さらにかみ砕いて言うと、プロセッサによるデータ読み出しを高速化するために、大抵のコンパイラでは、構造体の全体のサイズがレジスタサイズの整数倍になるように、詰め物(パディング)が勝手に挿入されることが多いです。

【1】
http://www.office-matsunaga.biz/evctips/evctips01.html

ちなみにVisual C++ 2010の既定のコンパイル オプションでテストしたら下記のようになります。
とりあえずclassの代わりにstructを使っていますが、基本的にどちらも一緒です。


str...続きを読む

QC++でのクラスオブジェクトの破棄

こんにちは。

C++では、プログラムの終了時に、全てのクラスオブジェクトは、デストラクタが呼び出されて破棄されますが、プログラムの途中で、クラスオブジェクトを明示的に破棄する方法はあるのでしょうか?

例えば、new演算子によってメモリを動的に割り当てたポインタなら、delete演算子で破棄できますが、
クラスオブジェクトにdelete演算子は使えないようです。

何かいい方法を知っておられる方がいらっしゃれば、是非アドバイスを頂きたいと思います。

Aベストアンサー

ClassA *p = new(buf) ClassA;

ClassA *p = new ClassA;
の意味は違います. 後者はメモリを割り当てますが, 前者ではメモリの割り当ては行われません. placement new なんかで調べてもらうといいかな.


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング