- PR -

VC++2005(CLI)でclass Aとclass Bを呼び合うコードの書き方

1
投稿者投稿内容
maru
ぬし
会議室デビュー日: 2003/01/27
投稿数: 412
投稿日時: 2007-01-22 17:34
お世話になってます。

現在、VC++2005でアプリケーションを開発しています。
VB.NETやC#、Javaなどのオブジェクト指向言語での開発経験はあります。
C++での開発もそれなりにあります。
が、いまさらこんなことで質問するのも恥ずかしいのですが、C++の書き方で
行き詰っています。

というもの、以下のコードのように、class A型の定義の中にはclass B型の変数が、
class B型の定義の中にはclass A型の変数が使われているようなとき、C++では
どのようにコードを書けばいいのかわかりません。

単純に、以下のようにコードを書くと、どちらかが型宣言できていないとなって、
コンパイルエラーになります。

VB.NETやC#、Javaではこんなこと意識したこともなかったのですが・・・。

コード:
---<A.h>---
#pragma once
#include "B.h"

class A{
   B b;
}

---<B.h>---
#pragma once
#include "A.h"

class B{
   A a;
}




上記は、わかりやすいように両者ともクラスとしましたが、今実際には
片側がインターフェースとなっているコードを書きたいのですが・・・。

googleで調べたのですが、検索キーワードが思い浮かばず、断念してしまいました。
よろしくお願いします。
Blue
大ベテラン
会議室デビュー日: 2005/09/12
投稿数: 230
お住まい・勤務地: 知っている人は知っている
投稿日時: 2007-01-22 17:43
参考:相互参照(循環参照)するクラス
http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=920240&SiteID=7

追記

純粋なC++でも考え方は同じです。
参考:includeについて
http://forums.belution.com/ja/vc/000/389/49s.shtml

#00038950のtetrapodさんのおっしゃることが全て。

[ メッセージ編集済み 編集者: Blue 編集日時 2007-01-22 17:53 ]
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-01-23 00:56
こんばんは。

引用:

maruさんの書き込み (2007-01-22 17:34) より:
上記は、わかりやすいように両者ともクラスとしましたが、今実際には
片側がインターフェースとなっているコードを書きたいのですが・・・。



C++には文法的な「インターフェイス」という概念はないのですが、
一般的には「純粋仮想関数のみをもつクラス」によって実現されます。
コード:
class A
{
	virtual void method() = 0;
};



次にC++ではJavaやC#などと同じく、
インターフェイスや抽象クラスのインスタンスを作成することはできません。
ただし、C++とJava,C#の文法上の違いから
この場合下記のように記述するとC++ではコンパイルエラーになります。
C++の場合、aは参照変数ではないからです。
コード:
class B {
	A a;	// Aは抽象クラス(インターフェイス)であるため、C++ではコンパイルエラーとなる。
};



参照やポインタであればエラーとはなりません。
# C++の参照はnullポインタをセットできないので、JavaやC#の参照とは少し違います。
  C++のポインタであれば、nullポインタをセットすることができます。

C/C++の場合、ソースコードを後方参照することができないので、
使用する場合は、予めクラスを宣言するか定義する必要があります。
参照・ポインタのみであれば、宣言のみでコンパイルすることができます。

コード:
class A;	// クラスAの宣言

class B {
	A* a1;	// a1はポインタ変数
	A& a2;	// a2は参照変数
};



では最後に
クラスAにクラスBへの関連を持たせるように、
クラスAを修正してみる場合、次の3通りができます。

パターン1
コード:
#include "B.h"	// B.h内でクラスBを定義

class A
{
	B b;	// 実体
	virtual void method() = 0;
};



パターン2
コード:
class B;	// クラスBの宣言

class A
{
	B* b;	// ポインタ
	virtual void method() = 0;
};



パターン3
コード:
class B;	// クラスBの宣言

class A
{
	B& b;	// 参照
	virtual void method() = 0;
};



パターン1〜3は、寿命や多重度で使い分けをします。

冬寂
ぬし
会議室デビュー日: 2002/09/17
投稿数: 449
投稿日時: 2007-01-23 12:59
ざっとしか読んでないんだけど、たぶん「プロトタイプ宣言」について調べてみるといいんじゃないかな?

# VCだと、afx.hにプロトタイプ宣言書いておくようなスタイルじゃなかったっけ?(使った事無いからいい加減に言ってるけど。)
Blue
大ベテラン
会議室デビュー日: 2005/09/12
投稿数: 230
お住まい・勤務地: 知っている人は知っている
投稿日時: 2007-01-23 13:06
引用:

# VCだと、afx.hにプロトタイプ宣言書いておくようなスタイルじゃなかったっけ?(使った事無いからいい加減に言ってるけど。)


違いますね。afx.hはMFCのヘッダですので書き換えてはダメなのでは。
stdafx.h(プリコンパイル済みヘッダ)なら変更してもいいですけど。

まぁ、stdafx.hに全てのプロトタイプ宣言を書くことはないですけどね。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-01-23 13:29
こんにちは。

ちなみに

引用:

maruさんの書き込み (2007-01-22 17:34) より:

コード:
---<A.h>---
#pragma once
#include "B.h"

class A{
   B b;
}

---<B.h>---
#pragma once
#include "A.h"

class B{
   A a;
}






すでに回答が出ていますが、
このようなことはできません。
クラスAはクラスBを包含して、クラスBはクラスAを包含しているとすると、
クラスAのインスタンスのサイズは無限大になってしまいます。

AオブジェクトはBオブジェクトを持ち…
そのBオブジェクトはAオブジェクトを持ち…
そのAオブジェクトはBオブジェクトを持ち…
そのBオブジェクトはAオブジェクトを持ち…
(繰り返し)

巨大(無限大)なオブジェクトになってしまいます。
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2007-01-23 14:21
引用:

Tdnr_Symさんの書き込み (2007-01-23 00:56) より:
参照・ポインタのみであれば、宣言のみでコンパイルすることができます。
コード:
class A;	// クラスAの宣言





試してみました。
なるほど〜、勉強になった。

引用:

maruさんの書き込み (2007-01-22 17:34) より:
class A{
  B b;
}


このように書くと、A型のオブジェクトのメモリ領域にBの実体を含めるような動きをします。
C#をご存知のようですので、「Bを値型のように扱う」といえば分かるかな。
C#やJavaではB型オブジェクトへの参照を持つだけですので、動きは全然違くなりますね。
理解してわざと書いているならいいんですけど。

あと、タイトルの(CLI)ですけど、内容的にはCLIじゃないですよね。
CLIなら
ref class A{
  B^ b;
}
こうかな。

[ メッセージ編集済み 編集者: 一郎 編集日時 2007-01-23 14:23 ]
maru
ぬし
会議室デビュー日: 2003/01/27
投稿数: 412
投稿日時: 2007-01-23 18:54
みなさん、アドバイスありがとうございました。
解決しました。

C++の経験は少しあったつもりでいたのですが、JavaやC#、VB.NETで慣れてしまった
状態でVC++(CLI)をやり始めたもので戸惑ってしまいました。

C++って、いろいろめんどくさいっすね。ヘッダとCPPファイルを分けたり。
でも、めんどくささに慣れればCLIはいろいろできるので面白くなってきましたけど。
1

スキルアップ/キャリアアップ(JOB@IT)