質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

構造体のメンバにスマートにアクセスしたい

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 559

score 5

前提

  1. ブロック崩しのような簡単なゲームを作成しようと思った
  2. ボールを表現するために移動するオブジェクト(block構造体)を作成しようと思った
  3. 四角いボールでよかったのでボール自体を表すrectangle構造体と、移動方向とそのスピードを表すvector2D構造体があればよいと思った
  4. それらを実装したが、実際に使用する際冗長な記述を強いられるため、自分の実装は間違っていると感じた
  5. 自らの知恵では解決できなかったため、質問をしようと思った

実現したいこと

構造体(vector2D, rectangle, block)とその関係を正しく定義し、冗長な記述をなくしたい

発生している問題・エラーメッセージ

  • 構造体のメンバへアクセスする際、コードが冗長になる
  • メンバへのアクセスを簡潔にすると、メンバへの演算の記述が冗長になってしまう
  • 理想とする使用方法を考えたが、それにも問題点があるのでもっと良い方法があればそれを知りたい

該当のソースコード

型の定義

struct vector2D  {
   int x;
   int y;
   /* vector2Dの演算子オーバロードを定義(+=, -=, *=, /=) */
}

struct rectangle { vector2D position; vector2D size; }
struct color { /* 内容に関係ないため省略 */ }

struct block {
   rectangle area; /* ブロックの表示領域(当たり判定) */
   vector2D speed; /* ブロックの移動速度 */
   color c;        /* ブロックが表示されるときの色 */
}

実装:現在のコード↓

block ball{ /* 実際の値は省略 */ };

void ball_update() //ボールの移動と表示
{
   vector2D ball_delta{ /* 実際の値は省略 */ } // 移動量の計算

   /* ボールの座標更新 */
   ball.area.position.x += ball_delta.x; // メンバ指定が冗長 (block.xとしたい)
   ball.area.position.y += ball_delta.y; // vecter2Dとして演算したい

   /* 各データへのアクセス */
   ball.area.position.x;
   ball.area.position.y;
   ball.area.size.x;
   ball.area.size.x;
   ball.speed.x;
   ball.speed.y;
   ball.c;

   /* ボール表示処理をする */
   return;
}

実装:理想のコード↓

block ball{ /* 実際の値は省略 */ };

void ball_update() //ボールの移動と表示
{
   vector2D ball_delta{ /* 実際の値は省略 */ } // 移動量の計算

   /* ボールの座標更新 */
   ball.position += ball_delta; // vector2D同士の演算

   /* 各データへのアクセス */
   ball.x; //ボールのx座標
   ball.y; //ボールのy座標
   ball.width;
   ball.height;
   ball.area.position.x;
   ball.area.position.y;
   ball.area.size.x;
   ball.area.size.x;
   ball.speed.x;
   ball.speed.y;
   ball.c;

   /* ボール表示処理をする */
   return;
}


理想のコードの問題点

  • ball.xball.position.xが同じものを指すことになる
  • ball.widthball.size.xが同じものを指すことになる

試したこと

struct blockのメンバをprivateにしてアクセス関数を書く

class block {
private:
   rectangle area; /* ブロックの表示領域(当たり判定) */
   vector2D speed; /* ブロックの移動速度 */
   color c;        /* ブロックが表示されるときの色 */
public:
   // コンストラクタ省略
   /* getter */
   int x()     { return area.position.x; }
   int y()     { return area.position.y; }
   int width() { return area.size.x; }
   int height(){ return area.size.y; }
   vector2D position(){ return area.position; }
   vector2D size()    { return area.size; }
   /* setter */
   void x(int x)     { area.position.x = x; }
   void y(int y)     { area.position.y = y; }
   void width(int w) { area.size.x = w; }
   void height(int h){ area.size.y = h; }
   void position(vector2D p){ area.position = p; }
   void size(vector2D s)    { area.size = s; }
}


問題点

  • block.x()block.position().xが同じものを指している
  • 関数と通じて値を変更するため、加算などの表現が分かりにくくなってしまう
  • vector2Drectangleblockを内包する構造体を定義したときに、アクセス関数をまた書く必要がある

補足情報(FW/ツールのバージョンなど)

Visual Studio 2019でやってます

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

そもそも論としては、外からデータメンバにアクセスしたいというのが悪い考え方です。 要素の内部にアクセスしなければならないというのはカプセル化に失敗していることの表れだからです。

データメンバの値を取りだして処理するのではなく、メンバ関数を呼び出す形でオブジェクトに対して「依頼」するのがオブジェクト指向の基本的な考え方です。 "Tell. Don't ask." という格言がよく知られています。 もちろんいつもそう理想的に出来るわけではありませんけども。

データを階層的に整理するところまでは良く出来ていると思うのですが、そのデータで何をするのかという振舞い (C++ では要するにメンバ関数のこと) を定義せずに非メンバ関数でまとめてやっているので、外から見ると深い階層構造になるのは仕方のないことです。

オブジェクト指向の考え方を捨てて C のような書き方をするのであればデータの階層化をやめた方が綺麗ですし、オブジェクト指向的に設計するのであれば値を取りだすのをやめてクラスの中 (のメンバ関数) で処理すべきです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/26 00:40

    この考え方で解決できそうです。
    ありがとうございました。

    キャンセル

0

ball.xとball.position.xが同じものを指すことになる
ball.widthとball.size.xが同じものを指すことになる

なんでball.xとball.position.xあるいはball.widthとball.size.xを分けよう、と思うのでしょう? それを分けようと思わなければ万事解決に思えてしまうのですが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/25 23:15

    1. ボールのx座標を表す、というときにball.xという記述だけで足りるのでball.xでボールのx座標にアクセスできればいいなと考えました
    2. ボールの座標をvecter2Dのまま扱えればオーバーロードした演算を使用できてx座標とy座標それぞれに演算を行わなくてよいのでball.positionにとしてアクセスできればいいなと考えました
    3. 結果的にボールx座標にはball.xとball.position.xの2つの方法でアクセスできることになってしまいます
    4. なので分けようと思ったわけではなく、やりたいことから考えた結果問題が発生してしまいました。

    質問1:分けなければ、というのはball.xというアクセス方法をせずにball.position.xという方法でアクセスすればよいということですか?
    質問2:アクセス方法一つにしたとしても、positionへのアクセスはball.rect.positionとアクセスしなければならず、冗長な記述をなくしたいという問題は解決していません。ball.positionとしてアクセスするにはどのような実装が良いと考えますか?

    キャンセル

  • 2020/01/25 23:27

    横からすみません。thkanaさんが言われているのは、「ball.xとball.position.xが同じものを指してはまずいのでしょうか。気にしなければいいのでは?」ということだと思いますよ。

    キャンセル

  • 2020/01/25 23:33

    ball.xとball.position.xが同じ変数を指しているのは混乱のもとだと思います。
    仮に気にしないとしてもすべての問題は解決しません。

    キャンセル

  • 2020/01/25 23:58

    ball.xとball.position.xとはそもそも同じものではないのですか、ということです。
    同じものを見え方?で分けようとするから混乱するのでは、と思えるのですが。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る