class A{
private:
AMap *p_a_map;
Unit *p_unit;
bool error;
public:
A(int number = 0);
void Update();
void Draw();
};
A::A(int number){
char *str;
switch(number){
case 0:
error = true;
break;
case 1:
str = "map1.csv";
break;
//マップの数だけ同じ処理
//ファイルのオープン
a_map.aa = //それぞれの項目を読み込み
p_a_map = *a_map;
}
}
クラスのメンバに別のクラスや構造体の配列を入れるときの話
クラスのメンバに別のクラスや構造体の配列を入れるときの話
ゲームでマップ作りをしているのですが、マップの情報やそこにいるユニットの情報などをメンバ配列として保存することでUpdateとDrawの二つで使用できるようにしたいのです。そしてなおかつその情報は外部ファイルから入力できるようにしたいのです。単純に言えば
と言った形でやって後からメンバの中身を設定して、mainの中でUpdateとDrawを使う形にしたいのですけどこれだとコンストラクタと言え、ローカル変数なので実体が消えてしまいますよね?だからと言ってUpdate本文にa_mapを作ってもDrawの処理に言った時にマップデータ消えてしまうし…どのようにしたらいいのでしょうか?説明が下手ですみません。一部処理は勉強中なので省いています
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
どのようにデータを読み込む領域を確保しているかがわからないので、わかりません。質問者A さんが書きました:これだとコンストラクタと言え、ローカル変数なので実体が消えてしまいますよね?
消えるデータなら消えます(消えない保証はないです)し、消えないデータ(静的変数など)なら消えないはずです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
std::vector<適切な型>型のメンバ変数に読み込む、コンストラクタでnewなどを用いて動的に領域を確保してデストラクタで開放する、などいろいろな方法が考えられます。質問者A さんが書きました:どのようにしたらいいのでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
データメンバの段階で動的にメモリを確保するということでしょうか?みけCAT さんが書きました:std::vector<適切な型>型のメンバ変数に読み込む、コンストラクタでnewなどを用いて動的に領域を確保してデストラクタで開放する、などいろいろな方法が考えられます。質問者A さんが書きました:どのようにしたらいいのでしょうか?
vectorもnewも関数内で行い、拡張してもstaticなしではメソッドが終わった時に拡張した値が消えてしまうのではないですか?
また、データメンバの段階で指定の大きさに動的に確保するにはどうすればいいのでしょうか?
static num のようなものを作り、mainで確保したい大きさをそこに入れてこれを利用してデータメンバ内で記述するのでしょうか
class A{
private:
static int num;
AMap *p_a_map = (AMap *)malloc(sizeof(AMap)*num);
}
読み出したファイルの指定するマップの大きさに合わせて作った動作用のAオブジェクト内のマップ配列の大きさを変えたいのです。その際、マップをそのAオブジェクト内においては消えずに使いまわせるようにしたいのです。
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
申し訳ないですが、よくわかりません。質問者A さんが書きました:データメンバの段階で動的にメモリを確保するということでしょうか?
「vectorを行う」「拡張」の意味がよくわかりませんが、newで確保した領域は一般にはメソッドが終わっても(確保したブロックを抜けても)勝手に消えません。質問者A さんが書きました:vectorもnewも関数内で行い、拡張してもstaticなしではメソッドが終わった時に拡張した値が消えてしまうのではないですか?
メモリリークを起こす可能性があるのはそのためです。
不可能と考えるのがいいかもしれません。質問者A さんが書きました:また、データメンバの段階で指定の大きさに動的に確保するにはどうすればいいのでしょうか?
不可能ではないと思いますが、かなり不自然な仕様だと思います。質問者A さんが書きました:static num のようなものを作り、mainで確保したい大きさをそこに入れてこれを利用してデータメンバ内で記述するのでしょうか
こうしてC++11モードでコンパイルすると通りましたが、個人的には素直にコンストラクタの引数でデータを渡したほうがいいと思います。質問者A さんが書きました:class A{
private:
static int num;
AMap *p_a_map = (AMap *)malloc(sizeof(AMap)*num);
}
#include <cstdio>
#include <cstdlib>
typedef int AMap;
class A{
private:
static int num;
//AMap *p_a_map = (AMap *)malloc(sizeof(AMap)*num);
int n = num;
public:
void print() const {
printf("%d\n",n);
}
static void set_num(int n_) {
num = n_;
}
}
;int A::num;
int main(){
A::set_num(10);
A a;
A::set_num(5);
A b;
a.print();
b.print();
return 0;
}
適当なサンプルを書いてみました。質問者A さんが書きました:読み出したファイルの指定するマップの大きさに合わせて作った動作用のAオブジェクト内のマップ配列の大きさを変えたいのです。その際、マップをそのAオブジェクト内においては消えずに使いまわせるようにしたいのです。
#include <cstdio>
typedef char MapChip;
typedef int Unit;
class AMap {
private:
int width, height;
int next_map;
MapChip *m;
public:
AMap(int w = 1, int h = 1) {
// w, hの値のエラーチェックは省略
width = w;
height = h;
m = new MapChip[w * h];
}
AMap(const AMap& a) {
width = a.width;
height = a.height;
next_map = a.next_map;
m = new MapChip[width * height];
for (int i = 0; i < width * height; i++) m[i] = a.m[i];
}
~AMap() {
delete[] m;
}
AMap& operator=(const AMap& a) {
width = a.width;
height = a.height;
next_map = a.next_map;
delete[] m;
m = new MapChip[width * height];
for (int i = 0; i < width * height; i++) m[i] = a.m[i];
return *this;
}
const MapChip& at(int x, int y) const {
return m[width * y + x];
}
MapChip& at(int x, int y) {
return m[width * y + x];
}
int get_width() const {
return width;
}
int get_height() const {
return height;
}
int get_next_map() const {
return next_map;
}
void set_next_map(int n) {
next_map = n;
}
static AMap load(const char *data) {
// ダミー
AMap map = AMap(10, 10);
const char *p = data;
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
map.at(x, y) = *(p++);
if (*p == '\0') p = data;
}
}
return map;
}
};
class A {
private:
AMap *p_a_map;
Unit *p_unit;
bool error;
public:
A(int number = 0);
~A();
void Update();
void Draw();
};
A::A(int number) {
const char *str;
p_a_map = NULL;
p_unit = NULL;
error = false;
switch(number) {
case 0:
error = true;
break;
case 1:
str = "map1.csv";
break;
default:
error = true;
break;
}
if(!error) {
// 読み込み処理のかわり
int map_num = 3;
p_a_map = new AMap[map_num];
p_a_map[0] = AMap::load("asumikana");
p_a_map[0].set_next_map(2);
p_a_map[1] = AMap::load("kitamuraeri");
p_a_map[1].set_next_map(-1);
p_a_map[2] = AMap::load("kugimiyarie");
p_a_map[2].set_next_map(1);
(void)str; // 警告避け
}
}
A::~A() {
if(p_a_map != NULL) delete[] p_a_map;
}
void A::Update() {
}
void A::Draw() {
puts("++++++++++");
if (p_a_map != NULL) {
for (int map = 0; map >= 0; map = p_a_map[map].get_next_map()) {
for (int y = 0; y < p_a_map[map].get_height(); y++) {
for (int x = 0; x < p_a_map[map].get_width(); x++) {
putchar(p_a_map[map].at(x, y));
}
putchar('\n');
}
puts("=====");
}
}
puts("----------");
}
int main(void) {
A a1 = 0;
A a2 = 1;
puts("draw a1");
a1.Draw();
puts("draw a2");
a2.Draw();
return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
独特な表現が多いようですが、独学ですか?
プログラミングを掲示板で教える上で文章が正しく通じないのは困りますので・・・。
プログラミングを掲示板で教える上で文章が正しく通じないのは困りますので・・・。
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
ええっと。C++は独学です。CとJavaをちょこっと習いました。変な表現になってしまうっているのは多分自分が簡単なことを深く考えすぎてわけがわからなくなってしまうタイプだからだと思います。hide さんが書きました:独特な表現が多いようですが、独学ですか?
プログラミングを掲示板で教える上で文章が正しく通じないのは困りますので・・・。
<1> <2>
class A{
pirivate:
B *p_b;
public:
A();
int getB();
}
A::A(){
B b;
p_b = &b;
}
int A::getB(){
return p_b->i;
}
<3>
class A{
pirivate:
B *p_b;
public:
A();
int getB();
}
A::A(){
p_b = new B();
}
int A::getB(){
return p_b->i;
}
今回<3>の例で実行できるということはnew B()によって動的にメモリを確保することでコンストラクタが終わっても解放されないため、p_bに入っているアドレスを使って対象を弄れるということであっていますか?動的確保をしていない<2>の例ではやはりエラーになるっと言う解釈であっていますか?あとvectorはB bみたいな扱いではなくて new B()と言ったように追加する配列を確保しているということでいいのでしょうか?
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
スペルミスを除けば、多分あっていると思います。質問者A さんが書きました:今回<3>の例で実行できるということはnew B()によって動的にメモリを確保することでコンストラクタが終わっても解放されないため、p_bに入っているアドレスを使って対象を弄れるということであっていますか?動的確保をしていない<2>の例ではやはりエラーになるっと言う解釈であっていますか?
微妙な気がします。確かにstd::vectorは配列を確保するはずですが、vectorならば必ず消えないというわけではなく、 のようなコードを書けば当然消えます。質問者A さんが書きました:あとvectorはB bみたいな扱いではなくて new B()と言ったように追加する配列を確保しているということでいいのでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
vectorであってもローカル関数で宣言してそのアドレスを使う場合はだめなんですね。メンバ変数で変数自体を作っておけばローカル関数内でpush_back()で値を追加しても消えないということですかね?みけCAT さんが書きました:スペルミスを除けば、多分あっていると思います。質問者A さんが書きました:今回<3>の例で実行できるということはnew B()によって動的にメモリを確保することでコンストラクタが終わっても解放されないため、p_bに入っているアドレスを使って対象を弄れるということであっていますか?動的確保をしていない<2>の例ではやはりエラーになるっと言う解釈であっていますか?微妙な気がします。確かにstd::vectorは配列を確保するはずですが、vectorならば必ず消えないというわけではなく、 のようなコードを書けば当然消えます。質問者A さんが書きました:あとvectorはB bみたいな扱いではなくて new B()と言ったように追加する配列を確保しているということでいいのでしょうか?
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 14年前
- 住所: 東海地方
- 連絡を取る:
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
プログラムを組むときにはバグが出来るだけ発生しない事を考えたほうが良いですね。
安全にvectorで扱うには、
1.ポインタで扱う場合はvectorよりも変数の寿命が長いことが保証されていこと。
2.インスンタンスの生成や消滅時に利用できないvectorが発生する恐れがないようにvectorの寿命と同一が出来るだけ望ましい。
3.生成順番を考慮しないと動かないようなプログラムはメンテ性が悪くなる(バグが発生しやしい)。
を頭においてください。
ローカル変数やメンバ変数は、クラス外に取り出されるなら避けましょう。
安全にvectorで扱うには、
1.ポインタで扱う場合はvectorよりも変数の寿命が長いことが保証されていこと。
2.インスンタンスの生成や消滅時に利用できないvectorが発生する恐れがないようにvectorの寿命と同一が出来るだけ望ましい。
3.生成順番を考慮しないと動かないようなプログラムはメンテ性が悪くなる(バグが発生しやしい)。
を頭においてください。
ローカル変数やメンバ変数は、クラス外に取り出されるなら避けましょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
std::vectorでは要素数の拡張時などにデータのコピーが発生するので、特にポインタを使う場合は誤って消さないように注意が必要です。
×コピー時にポインタの値のみをコピー(シャローコピー)→デストラクタでコピー元のポインタが指すデータを開放する→コピー先のデータ中のポインタも開放した場所を指している(死亡)
○コピー時にポインタが指す内容をコピー(ディープコピー)デストラクタでコピー元のポインタが指すデータを開放する→コピー先のデータ中のポインタは新たに確保した場所を指しているのでOK
×コピー時にポインタの値のみをコピー(シャローコピー)→デストラクタでコピー元のポインタが指すデータを開放する→コピー先のデータ中のポインタも開放した場所を指している(死亡)
○コピー時にポインタが指す内容をコピー(ディープコピー)デストラクタでコピー元のポインタが指すデータを開放する→コピー先のデータ中のポインタは新たに確保した場所を指しているのでOK
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
シャローコピーは同じものを指すコピーでディープコピーは別々のものを指すコピーですね。vectorのコピーはポインタを使うとシャローになってしまうのでポインタを使わない方法でコピーを取ればOKということですかみけCAT さんが書きました:std::vectorでは要素数の拡張時などにデータのコピーが発生するので、特にポインタを使う場合は誤って消さないように注意が必要です。
×コピー時にポインタの値のみをコピー(シャローコピー)→デストラクタでコピー元のポインタが指すデータを開放する→コピー先のデータ中のポインタも開放した場所を指している(死亡)
○コピー時にポインタが指す内容をコピー(ディープコピー)デストラクタでコピー元のポインタが指すデータを開放する→コピー先のデータ中のポインタは新たに確保した場所を指しているのでOK
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
「vectorのコピーはポインタを使うとシャローになってしまう」というのは違うと思いますが、質問者A さんが書きました:vectorのコピーはポインタを使うとシャローになってしまうのでポインタを使わない方法でコピーを取ればOKということですか
ポインタを(プログラマからの見た目で)使わないというのは無効なデータが含まれないための十分条件である(必要条件とは限らない)気がします。
オフトピック
x86のCPUレベルでは、レジスタに収まる小さいデータでない限りはポインタを使わないとコピーできないと思いますが…
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
違うんですか?むむむ…なんかわけがわからなくなってきました。みけCAT さんが書きました:「vectorのコピーはポインタを使うとシャローになってしまう」というのは違うと思いますが、質問者A さんが書きました:vectorのコピーはポインタを使うとシャローになってしまうのでポインタを使わない方法でコピーを取ればOKということですか
ポインタを(プログラマからの見た目で)使わないというのは無効なデータが含まれないための十分条件である(必要条件とは限らない)気がします。オフトピックx86のCPUレベルでは、レジスタに収まる小さいデータでない限りはポインタを使わないとコピーできないと思いますが…
…もう一度、本などで勉強し直したほうが良いかもしれません…でも言語の勉強本を見てもこういう風にすればいいと言うのはよく書いてあるんですけど、こういう風にするとこういう風になるからダメだよってことが詳しく書いてあるの少ないんですよね…。
そう言ったことをどう勉強すればいいのでしょうか?おすすめ本があれば教えていただけますか?
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
オフトピック
std::vectorなどは便利だけれども,
「std::vectorもどきを自分で書ける = なんとなくvectorの実装ってこういうことになってるんだろうな,という想像がつく」
くらいになってから使う方がいいと思う.個人的には.
最初はnewとdelete等を使ってなんでもかんでも自前で生存期間を管理するように書いてみればどうでしょう.
>こういう風にするとこういう風になるからダメだよってこと
本などで読んで理解できればそれでいいですが,例えば,
みたいなこととか,一度実際に経験してみた方が身に染みて理解できるかも?
「std::vectorもどきを自分で書ける = なんとなくvectorの実装ってこういうことになってるんだろうな,という想像がつく」
くらいになってから使う方がいいと思う.個人的には.
最初はnewとdelete等を使ってなんでもかんでも自前で生存期間を管理するように書いてみればどうでしょう.
>こういう風にするとこういう風になるからダメだよってこと
本などで読んで理解できればそれでいいですが,例えば,
//実行時に要素数を決定できるint配列 みたいな「つもり」でこれを書いたとして…
class MyArray
{
public:
MyArray(){ m_Array = nullptr; m_size=0; }
~MyArray(){ delete[] m_Array; }
void resize( size_t size )
{
delete[] m_Array;
m_Array = new int[ size ];
m_size = size;
}
int &operator[]( int i ){ return m_Array[i]; }
int operator[]( int i ) const { return m_Array[i]; }
size_t size() const { return m_size; }
private:
int *m_Array;
size_t m_size;
};
//
int main()
{
MyArray A1;
{
MyArray A2;
A2.resize( 10 );
A1 = A2; //おおっと…
}
return 0;
}
Re: クラスのメンバに別のクラスや構造体の配列を入れるときの話
コピーコンストラクタがない動作を経験してみるってことですねusao さんが書きました:オフトピックstd::vectorなどは便利だけれども,
「std::vectorもどきを自分で書ける = なんとなくvectorの実装ってこういうことになってるんだろうな,という想像がつく」
くらいになってから使う方がいいと思う.個人的には.
最初はnewとdelete等を使ってなんでもかんでも自前で生存期間を管理するように書いてみればどうでしょう.
>こういう風にするとこういう風になるからダメだよってこと
本などで読んで理解できればそれでいいですが,例えば,みたいなこととか,一度実際に経験してみた方が身に染みて理解できるかも?//実行時に要素数を決定できるint配列 みたいな「つもり」でこれを書いたとして… class MyArray { public: MyArray(){ m_Array = nullptr; m_size=0; } ~MyArray(){ delete[] m_Array; } void resize( size_t size ) { delete[] m_Array; m_Array = new int[ size ]; m_size = size; } int &operator[]( int i ){ return m_Array[i]; } int operator[]( int i ) const { return m_Array[i]; } size_t size() const { return m_size; } private: int *m_Array; size_t m_size; }; // int main() { MyArray A1; { MyArray A2; A2.resize( 10 ); A1 = A2; //おおっと… } return 0; }