CListを含む構造体のリストをメンバ変数にしたい

[戻る]
YM555 2009/05/13(水) 15:46:55
開発環境
WindowsXp SP3
VisualStudio2005
MFCにてSDIアプリケーションを作成

以下の2つの構造体を定義して

typedef.hにて
struct DATA
{
    int    iYear;
    int    iMonth;
    int    ibyDay;
    int    iData;
};

struct DATA_CONT
{
    int    iData1;
    int    iData2;
    int    iData3;
    CList< DATA, DATA & > CListData;
};

app.h(アプリケーションクラスのヘッダ)にて
    CList<DATA_CONT,DATA_CONT &> m_CListDataCont;
このようにCListを含む構造体のリストを定義しますと
・・・・afxtempl.h(781) : error C2248:・・・・(エラーが長文のため省略)
が表示されます。
DATA_CONTの定義を
   CList< DATA, DATA & > CListData;から
   CList< DATA, DATA & > * pCListData;
とポインタにすれば回避できることはわかったのですが
なぜ、ポインタにしない場合エラーが表示されるかがわかりません。

理由の判る方、アドバイスをお願いします。

επιστημη [E-Mail] [HomePage] 2009/05/13(水) 15:53:12
CList<> が operator= 持ってないから、とか?

YM555 2009/05/13(水) 17:19:54
επιστημηさん、早速の回答ありがとうございます。
たしかにエラーメッセージを見ますとCListクラスにはoperate=が無く
CObjectクラスのoperator=を継承しているようで、このoperator=がprivateに
あるためアクセスできませんとの記述があります。
そこでわからないのが
1. なぜCListクラスがOperator=を持っていないことがエラーの原因となるので
   しょうか?
2. このような使用方法をする場合はCListクラスを基底クラスとして
   operate=を独自で定義したクラスを事前に用意して使うのでしょうか?

仲澤@失業者 2009/05/13(水) 19:25:23
割り込み失礼します。m(__)m。
コピーコンストラクタも止めてるので、これは、

「不注意によるバグの発生を止めている」

という意図を強く感じます。 > CObject
自分も不用意にこれらが使用されたときのバグを回避するのに
似たような方法を使ってますです(マクロ使ってます)。

PATIO 2009/05/14(木) 11:53:38
テンプレートクラスは色々な物が入る可能性があるので
単純な代入で良いかどうかの判断が出来ないからわざと実装していない。
と言うのも有るかと。

今書かれているCListは、DATA_CONTを実体として保持するようになっています。
と言う事は、SetAt等の関数で引き渡されたDATA_CONTのインスタンスから
CList内のDATA_CONTのインスタンスに代入するというオペレーションが発生します。

構造体として定義しているのあれば、DATA_CONTはoperator=を実装していない
のではないかと思います。
この場合、DATA_CONTの代入は単純なメンバーの代入で処理されますから
CListDataも単純な代入で処理されてしまいますね。
ところがCListの代入演算子は定義されていないので代入できません。
だから、エラーが出ます。

CListが代入演算子を定義していないのは前述の通り、どんな物が入っているのか
分からないのでどんな物が来ても大丈夫な代入演算子を定義するのは無理だから。
で、CObjectのoperator=がprivateである事で代入演算子が未定義である事が
検出できるようにしていると私も思います。

本来は、DATA_CONTに代入演算子(operator=)を実装するのが筋でしょう。
テンプレートクラスで実体で使うようなものは代入演算子を実装するべきです。
DATA_CONT::operator=の中でCListData同士で一つずつ取り出して代入するように
記述していれば、問題ないと思います。

まあ、CListがあまりにも多量のデータを保持するようになると代入自体が
遅くはなるでしょうね。

PATIO 2009/05/14(木) 11:56:42
ちなみにCList< DATA, DATA & > * pCListData;でやってしまうと
最初に確保された領域が持ち回されるので気をつけないと開放済みの
インスタンスを参照しかねません。管理がかなり面倒だと思います。
単純代入で処理されてしまうからそういう事になります。

PATIO 2009/05/14(木) 12:02:16
あと、テンプレートの代入演算子を特殊化して定義すれば、
対応可能だったと思います。

YM555 2009/05/14(木) 13:45:29
[[解決]]
仲澤@失業者さん、PATIOさん、回答ありがとうございます。

operate=が必要な理由および、不注意によるバグ発生の抑止の意味が
ようやく判ってきました。
自分の作成するソフトでは構造体内のCListDataへの代入処理は
行わないため、なぜoperate=が必要なのか判りませんでしたが
よくよく考えれば、構造体への代入やメンバへの代入が行われる
可能性がある以上、operate=が必要になるのですね。

現状ソフトは小規模のためpCListDataでやっても管理はやって
いけそうです。
ただ、PATIOさんに指摘していただいた危険性ももっともだと
思いますので、時機を見て修正を行いたいとも思います。

みなさん、貴重なアドバイスありがとうございました。
これにて解決とさせていただきます。

bun 2009/05/14(木) 14:06:55
知りたいのは、なぜ operator= が必要なのか、ですよね?

afxtempl.h 内の
  template<class TYPE, class ARG_TYPE>
  POSITION CList<TYPE, ARG_TYPE>::AddHead(ARG_TYPE newElement)
関数内において、
 pNewNode->data = newElement;
の行があります。

テンプレートなので、ソースコードが見づらいですが、よーく注意して
見てみると、operator= 呼び出しなのが分かるかと思います。

んで、CObjectクラスは、operator= を private殺し してるから、
エラーとなるわけです。

それ以降の話は、PATIOさんの話通り。

なお、DATA_CONT構造体において、operator= を再定義しても、
コンパイルエラーは無くなります。
(struct は 基本 public の class だということは OK ですよね?)

bun 2009/05/14(木) 14:08:37
失礼、入れ違いになりました。

bun 2009/05/14(木) 14:17:28
一応、結論を述べておきます。
  template<class TYPE, class ARG_TYPE = const TYPE&>
  class CList : public CObject
において、
  ARG_TYPE型 から TYPE型
への代入演算が必須と言うことです。

逆にいうと、代入演算ができればいいだけなので
  CList<CString, LPCTSTR>
なんていうのも有効です。

同様に
  CArray<CString, LPCTSTR>
  CMap<CString, LPCTSTR, INT, INT>
も有効です。

YM555 2009/05/19(火) 16:40:48
bunさん、入れ違いになってしまいました。
レス、ありがとうございます。

色々と試してみまして、下記のように定義してうまくいきました。

struct DATA
{
    int    byYear;
    int    byMonth;
    int    byDay;
    int    byData;
};

class CDATA_CONT 
{
public :
    int    byData1;
    int    byData2;
    int    byData3;
    CList< DATA, DATA & > CListData;

public:
    void operator=(CList<DATA, DATA &> CListData ){//=オペレータは何もしません
    }
};

これでビルドエラーが出なくなり、CListDataを使用したデータ管理も
うまくいきました。

みなさん、助言ありがとうございました。

仲澤@失業者 2009/05/19(火) 17:57:41
エラーは出ないかも知れませんが、正しく動作しない可能性がありますね。
operator=()を使用している、CList<DATA_CONT,DATA_CONT &>のメンバ関数は
だめでしょう。

うまくいかなかったら
  void operator=(CList<DATA, DATA &> CListData ){
   // ブレークポイント
  }
して、何をすべきか検討してみてください。

PATIO 2009/05/20(水) 16:36:13
PATIOです。
なんか、話が進んでいますが、

> class CDATA_CONT 
> {
> public :
>     int    byData1;
>     int    byData2;
>     int    byData3;
>     CList< DATA, DATA & > CListData;
> 
> public:
>     void operator=(CList<DATA, DATA &> CListData ){//=オペレータは何もしません
>     }
> };

違います。
これでは本来の目的が果たせませんし、
この実装では意味が無いと思います。

もし、class CDATA_CONTにoperator=を実装するのでしたら

CDATA_CONT& CDATA_CONT::operator=(const CDATA_CONT& cont)

を実装するのが、順当でしょう。

C++言語の入門書等をよく読んで、operator=の実装について勉強した方が
良いと思いますよ。operator=の実装はちゃんとやっておかないと
変な不具合が起こって追いかけるのが大変になりますから。

YM555 2009/05/26(火) 16:19:19
仲澤@失業者さん、PATIOさん、回答ありがとうございます。
私が勘違いをしておりまして、PATIOさんのおっしゃる通りこれでは
解決策になりませんでした。
ビルドすら通りません。
色々と調べてみまして、オペレータを下記のように実装しなおしたところ
うまくいきました。
class CDATA_CONT 
{
public :
    int    byData1;
    int    byData2;
    int    byData3;
    CList< DATA, DATA & > CListData;

public:
    CDATA_CONT & operator=(CDATA_CONT & copy ){
        byData1 = copy.byData1;
        byData2 = copy.byData2;
        byData3 = copy.byData3;
        CListData.RemoveAll();
        CListData.AddTail( &copy.CListData );
        return *this;
    }
};
現状ではconstを指定するとビルドエラーとなるため、PATIOさんの
示していただいた通りには実装していません。

>>C++言語の入門書等をよく読んで、operator=の実装について勉強した方が
>>良いと思いますよ。operator=の実装はちゃんとやっておかないと
>>変な不具合が起こって追いかけるのが大変になりますから。

まったくその通りですね、ビルドが通らない件も含めて、再度勉強してみたいと
思います。

古いスレッドには追加発言できません。