c++言語においての質問です.
A, Bという二つのクラスを作ったとします.
宣言は.h,実装は.cppで行っています.
【A.h】
class A{
//内容
public: B ConvertB();
};
【B.h】
class B{
//内容
public: A ConvertA();
};
上のように,クラスAではクラスBを返すメソッド.
クラスBではクラスAを返すメソッドを実装したいとします.
しかし,単純に
【A.h】に #include "B.h"
【B.h】に #include "A.h"
などとすると当然循環参照になってエラーになりますよね?
...とは言っても,
【A.cpp】や【B.cpp】にインクルードした所で,
ヘッダ側でエラーが起きてしまいます.
そこで,私の場合は...
【A.h】の先頭に class B{ConvertAで用いるメソッド};
【B.h】の先頭に class A{ConvertBで用いるメソッド};
というようにする事でなんとかエラーを避けています.
...しかし,この方法ではAやBのクラスの定義を修正する場合に,
2つのファイルのヘッダを書き換えなければならなくなります.
A, B, C, D...などとクラスが増えていくと,
一つのヘッダーファイルに4つも5つもクラスの定義を書かなければなりませんし,
クラスのメソッドの定義を一つ変えようとしただけでも,
複数のヘッダの内容をいじらないといけません.
非常に読みにくいコードになってしまうのです.
そこで,もう少しスマートに実現する方法は無いでしょうか?
JavaやC#を使えば簡単に解決するのですが...ライブラリの関係でC++を使わなければ
ならないのです.
もしくは,このような相互変換?のクラスを作る場合はみなさんはどのようにして
ヘッダーファイルの循環参照を避けているのでしょうか?
例えば様々な形式の色空間のクラスなどだったら...
RGBクラス
YCrCbクラス
HSVクラス
...など
このようなクラスを作った後で,RGBをYCrCbに変換出来るようにしたり,
その逆へも変換出来るようにしたいのです.
それともこのような変換が出来るクラスを作る事は間違っているのでしょうか?
よろしくお願いします.
A 回答 (6件)
- 最新から表示
- 回答順に表示
No.6
- 回答日時:
結局のところ「ヘッダに書いてある前方宣言に,さらにコンストラクタやメソッドなどまで定義してしまっていて」というところが問題になってるだけ?
もしそうなら, 「ヘッダに書かなければいい」だけじゃないの?
No.5
- 回答日時:
とりあえず #1 は書いたんだけど, 実のところ「クラスのメソッドの定義を一つ変えようとしただけでも,複数のヘッダの内容をいじらないといけません.」が何を言っているのかさっぱりわからなかったのでそこは無視してたりします. 具体的に「こんな風に困る」ってのがあればコメントするかも.
で, 私も #3 同様何か構成が不自然なんじゃないかなという気はしました. それが「クラスの作り方」なのか, 「ファイルの作り方」なのかはちょっとわかりません. 例えば, 「『相互変換』に意味がある」ということは, それらのクラスは本質的に同じものを表している可能性があります. だとしたら, その「本質的に同じもの」を切り出すべきなのかもしれません.
あるいは「本質的に同じものなんだから 1つのファイルで宣言/定義する」というのも 1つの考え方.
やっぱりクラス設計が悪そうですね><
こんな回りくどい方法でしかエラーを回避出来ない事自体で,構造を見直すべきなのかもしれません><
「本質的に同じもの」というのは確かに似ているクラスもあるので,継承をうまく使えばスマートな方法があるかもしれませんね.
一度考えてみます
回答ありがとうございました^^
No.4
- 回答日時:
ヘッダファイルに宣言だけを含めるのであれば
// AB.h
class A;
class B;
class A{
public:B convert();
};
class B{
public:A convert();
};
を作ればA, Bの定義や引用で共通に使えます。
// A.cpp
#include "AB.h"
B A::convert(){ return B(); }
// B.cpp
#include "AB.h"
A B::convert(){ return A(); }
// main.cpp
#include "AB.h"
int main(void) {
A a;
B b=a.convert();
a=b.convert();
}
「一つのファイルに一つのクラス」という呪縛にとらわれていました><
確かに,一つのファイルに複数のクラスを書いても良い場合は結構ありそうですね.
それも視野に入れて,一度考え直してみます.
回答ありがとうございました^^
No.3
- 回答日時:
おそらくは、クラスの作り方が適切でないという可能性が高いです。
本来、クラスの循環参照は、単純に、クラスの「前方宣言」をつかうことで解消できるレベルに抑えられるようになっています。
ここで、質問にあるように、
> 【A.h】の先頭に class B{ConvertAで用いるメソッド};
> 【B.h】の先頭に class A{ConvertBで用いるメソッド};
という、calss B; だけでなく(これだけを宣言しておくのが、前方宣言です)メソッドまで宣言しなければ、やりくりできないというのは、良い設計ではないです。
基本的に、クラスの、メンバ関数は、「自分のクラスの中にある情報だけで、処理ができるもの」であるべきです。
もちろん、例外はありますが、基本はそういうことで。
この意味で、クラスというのは、「とにかく関わりのありそうな処理をぜんぶまとめておく」というものではないです。
で、この場合、単純に、
A converA(B var);
という関数を、クラスの外に定義すれば、非常に見通しが良くなると思いますが。
covnertA() を、Bのメンバ関数にするのは、Bのメンバだけで、すべて処理ができるときに限定した方が、見通しは良くなります。
なお、クラスのメンバ関数にしなくても、例えば、
A convertA(B var);
A convertA(C var);
と、いろいろなクラスから、A に、converA() という同じ名前の関数で変換できるような関数を定義するのは、C++ で可能であることに注意してください。
また、convertA(B var); が、class B の private: メンバーにアクセスする必要があるなら、covnertA() を、classB の friend 関数にすればOKです。
このごろJavaやC#を用いる事が多かったもので,クラスの外で関数を使う事を極端に嫌っていました><
C++でこのような循環参照がよく起るのは,逆にこのような場合は外で関数を使いましょうと言う事なのかもしれないですね><
一度構造を見直してみます.
前方宣言だけで解決出来る方法も同時に考えてみたいと思います.
回答ありがとうございました^^
No.2
- 回答日時:
-----------------------------------
A.h
-----------------------------------
#pragma once
class B;
class A
{
public:
B b();
};
-----------------------------------
B.h
-----------------------------------
#pragma once
class A;
class B
{
public:
A a();
};
-----------------------------------
A.cpp
-----------------------------------
#include "A.h"
#include "B.h"
B A::b()
{
return B();
}
-----------------------------------
B.cpp
-----------------------------------
#include "B.h"
#include "A.h"
A B::a()
{
return A();
}
私も現在の所,これに似た方法をつかっています^^
ただし,ヘッダに書いてある前方宣言に,さらにコンストラクタやメソッドなどまで定義してしまっていて...
もう少しすっきり出来るように構造を考えてみます^^
回答ありがとうございました^^
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Java java 飾子を付けること(public static・・・) ・コンソールへの出力処理はmainメ 2 2022/06/16 19:34
- Java javaのクラスの分け方について質問です。 APIの内部用と外部用でクラスを分けたいのですがインター 2 2022/04/26 16:06
- その他(プログラミング・Web制作) Pythonで複数のメソッドをまとめて管理する方法について 1 2023/03/30 00:01
- その他(プログラミング・Web制作) pythonのプログラムについての質問です。 1 2023/05/26 10:31
- Java JavaのSingletonパターンのprivateの持つ意味が分かりません。 5 2022/06/12 10:38
- その他(プログラミング・Web制作) どういうプログラムで組みますか?google colabでやってるんですけど、出来る方お願いします。 1 2022/07/17 18:41
- Java javaの質問です 次の機能を有するメソッド4つを自クラスに作成し、実装したいです 【機能】 足し算 1 2022/06/15 17:49
- Java java 次の機能を有するメソッドを自クラスに作成し、実装したいです。 機能 名前判定機能 →名前が 3 2022/06/16 16:08
- その他(プログラミング・Web制作) どういうプログラムで組みますか?google colabでやってるんですけど、出来る方お願いします。 1 2022/07/06 09:28
- PHP アップロード画像数でCSSを分けることに成功したのですが、画像の横に文字を並べることが出来ません。 3 2023/07/28 17:16
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
抽象クラスをJUNITでテストする...
-
「ラッパークラス」の存在意義...
-
JTextFieldの入力制限
-
(vba)他のアプリケーションの右...
-
interface,extend,implementの...
-
Javaで下線
-
スタックで成績表を作るプログ...
-
バックグラウンドでキー監視
-
サブクラス型オブジェクトをス...
-
C言語のプログラムをJavaに
-
VB初心者なのでわかりづらい説...
-
Javaでタスクシステムを作るの...
-
Javaの自作例外クラスについて
-
親クラスのメソッドを別のパッ...
-
XMLデータの受信
-
Javaでは多重継承ができない、...
-
Java難しすぎ
-
「インターフェイス」って何の...
-
vb.net 自作プロパティの削除に...
-
Class.forName("org.postgresql...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
interface,extend,implementの...
-
(vba)他のアプリケーションの右...
-
抽象クラスをJUNITでテストする...
-
「ラッパークラス」の存在意義...
-
ASP.NETでの共通コードの書き方...
-
JTextFieldの入力制限
-
「継承されたメソッドの可視性...
-
VB DLLプロジェクトについて
-
C# 「データが失なわれる可能性...
-
c++でのヘッダーファイルの循環...
-
【C#】クラスのコンストラクタ...
-
「IOException は対応する try ...
-
メソッドの引数にクラス名を渡す
-
ファイルパスが取得出来ない(P...
-
ゲッターを使わないで変数にア...
-
Javaのインスタンス化の構文の...
-
オーバーライドとラッパーの違い
-
vb.net 自作プロパティの削除に...
-
なぜインタフェースを使うのか?
-
VBがオブジェクト指向言語でな...
おすすめ情報