【初心者歓迎】C/C++室【環境依存OK】

このエントリーをはてなブックマークに追加
952949:03/10/25 16:01
>>951
あぁぁぁぁ!ありがとうございます!
これで1週間以上も足止め食ってたのが哀しすぎる…。

もうやろうとしてたことが最終局面なんですけど、最後

検索で見つかった各ファイルに先ほどセットしたシステムイメージリスト内の
アイコンを割り振っていくはどうしたら良いのでしょうか?
見つかったファイルと肝心のImageIndexの値とをどうやって結びつけるのかが
わかりません。

ListView1->Items->Item[0]->ImageIndex = ???;

やや丸投げっぽくて申し訳ありませんがよろしくお願いします
953949:03/10/25 17:02
あれ?今やってみたら

ListView1->Items->Item[0]->ImageIndex = ???;

の???の部分にどの数字を入れてもこの↓アイコンが表示されてしまう。
http://cgi.members.interq.or.jp/www1/q-mail/gh/img-box/img20031025165307.gif

試しにさっき捕まえたシステムイメージリストにイメージがいくつか入ってたのか調べてみると

int nCnt = ImageList_GetImageCount(hImageList) ;
ShowMessage(IntToStr(nCnt) );

5個しか入ってませんでした…。

あれ?どういうことなんでしょうか?あと一歩のところだと思ってたのに。
954949:03/10/25 21:16
あ、できました。

// ListViewにファイル、フォルダを表示するコールバック関数
WIN32_FIND_DATA fd; // 列挙情報取得用
HANDLE hFind; // 検索用のハンドル
SHFILEINFO fileinfo;
AnsiString Pattern = DirName + "\\*.*"; // ワイルドカードで全ファイル検索

hFind = FindFirstFile( Pattern.c_str() , &fd );// 最初のファイルを検索

if( hFind != INVALID_HANDLE_VALUE ){

do{ // ファイルが無くなるまで続ける
// . と .. は無視する
if( strcmp( fd.cFileName , "." ) != 0 && strcmp( fd.cFileName , ".." ) != 0 ){
if( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ){// フォルダ属性か調べる
// 現在のフォルダに見つかったフォルダを追加
AnsiString SubFolder = DirName + "\\" + fd.cFileName;
if(CheckBox3->Checked)EnumFolders( SubFolder , Items );// サブフォルダを検索する
if(CheckBox2->Checked){ //フォルダもリストに加える場合の処理
Items = ListView1->Items->Add();
Items->Caption = fd.cFileName;

AnsiString Folder =ExtractFileDir( fd.cFileName );

::SHGetFileInfo( fd.cFileName, NULL, &fileinfo,
sizeof(fileinfo),SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON );

Items->ImageIndex = fileinfo.iIcon;
}
}
955949:03/10/25 21:23
else{
// アイテムを追加
if(CheckBox1->Checked){
Items = ListView1->Items->Add();
Items->Caption = fd.cFileName;

AnsiString Ext =ExtractFileExt( fd.cFileName );
SHFILEINFO fileinfo;
::SHGetFileInfo( Ext.c_str(), NULL, &fileinfo,
sizeof(fileinfo),SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON );

Items->ImageIndex = fileinfo.iIcon;
}
}
}
}
while( FindNextFile( hFind , &fd ) == true );
// ハンドルを閉じる
FindClose( hFind );
}


大分早くなりましたが、ただ、ファイル数が500個を超えると
まだ他の処理を行わせることを考えればちょっと遅く感じます。
もっとスマートにListViewに表示させるのにはどのへんを変えればいいんでしょうか。

あと見つかったファイルがフォルダの場合もちゃんとIconIndex数に3という数字が返ってくるんですが、
調べてみるとシステムイメージリストのフォルダアイコンのインデックス数は
ウチのXPでは4になってるみたいなんですが、これは
Items->ImageIndex = fileinfo.iIcon+1;
という風に1を足してやらないといけないんでしょうか?
ソースを読もうと思ったけど、途中で嫌になった・・・。

とりあえず、2点だけ。
「.」「..」をスキップする処理はループから出せ。ループするたびにチェックするのは全くの無駄。
取得した拡張子ごとのアイコンのindexを記録しておけ。ファイルごとにSHGetFileInfoするのは無駄。
957949:03/10/25 23:25
>>956
ああ、すいません、そんな馬鹿ソースだったとは…。
チェックボックスによる条件分岐とか、関係ないものを削除するの忘れてましたぁ。
とりあえずおっしゃった二点を修正してみようと思います。
割り込みになってしまって失礼します。
今、抽象クラスと多重継承で頭を悩まされています。
アドバイスしてもらえると助かります。

以下のように

class AbstractA {
 virtual void func() = 0
};

class ConcreteA {
void func() { ....; return; }
};

class SubA : public AbstractA, pubic ConcreteB {
};

main(){
 SubA a;
 a.func(); //ConcreteB::func()が呼ばれる?
 return 0;
}
(一旦切ります)
>>958の続き)

AbstractAをメンバ関数名の継承のみに使い、
ConcreteAを継承することで初めてSubAが
課せられたメンバ関数の実体を持つようにしたいのですが、
上記の状態ままでは、ConcreteAとAbstractAのどちらのfunc()を
呼んで良いか決定できずコンパイルエラーになりました(VC++6で)。

とりあえず、SubAとConcreteAでAbstructAを仮想継承してみることで、
望むとおりConcreteA::func()を呼び出すようになったのですが、
「2つ以上のメンバが同じ名前を持っています。'ConcreteB'から継承します」云々
のような警告を出されてからです。
他に方法はないが危険な方法なので警告されているだけならよいのですが、
もっと正当な実現方法があるのなら教えてもらえないでしょうか?

よろしくお願いします。
960958:03/10/25 23:48
あ、、、少し単純化して書きすぎたかも、
ConcreteAクラスの方には、メンバ変数が定義されていて
funcはそれを利用しています(ほとんどメンバのGetterとSetterなんですが)。
なので多重継承を考慮するとインターフェイスと実体を分離して扱いたいのです。
補足失礼。
>>957
コードそのものは悪くないよ。
ただ、第三者には判りようがないオブジェクトが入っているのと
インデントがないのと
どうやら中カッコが正しく照応していないらしいのが問題。
この辺は人にコードを見せるときは気ぃつけてね。
>>958
をい、ConcreteBってなんや?
それを示さんと他人にはチンプンカンプンだぞ。
おわ。ひどい間違いを……

ConcreteB -> ConcreteAです
実際に試しているのは、

AbstactSize{
public:
 virtual const Size& GetSize() const = 0;
 virtual void SetSize(const Size&) = 0;
};

ConcreteSize{
protected:
 Size size;
public:
ConcreteSize() : size() {
 }
 virtual const Size& GetSize() const {
  return size;
 }
 virtual void SetSize(const Size& size) {
  this->size = size;
 }
};

Sizeはint[2]のラッパークラスです。

またもや申し訳ない。
>>963
ConcreteSize は AbstactSize を継承してるわけではないのか?
普通、具象クラスは抽象クラスのサブクラスとして作ると思うんだが。

多重継承で指定したスーパークラス呼びたいなら
クラススコープで指定して呼べば?
>ConcreteB -> ConcreteAです
意味不明。
アローの意味ぐらい知ってるよな?
ConcreteAはConcreteBのメンバなのか?

自分言葉で書くな。
だれでも判るきちんとした日本語、きちんとした表現で書け。


何を言いたいかさっぱり分からないから以下はあてずっぽう。
class SubA : public ConcreteA {
にする。
AbstractAを継承する必要はない。
>959
> 上記の状態ままでは、ConcreteAとAbstractAのどちらのfunc()を
> 呼んで良いか決定できずコンパイルエラーになりました(VC++6で)。
俺のところ試してみたら、
「AbstractAから継承した仮想関数の実体がない」
と言われたんだが。

> とりあえず、SubAとConcreteAでAbstructAを仮想継承してみることで、
この状況でどうやって仮想継承ができるの?

>965
>ConcreteB -> ConcreteA
これぐらい読みとってやってもいいんでないの?
class名にアローが使える訳ないんだし。んで、これならOK? => or →
>>966
>>ConcreteB -> ConcreteA
>これぐらい読みとってやってもいいんでないの?
>class名にアローが使える訳ないんだし。んで、これならOK? => or →

それなら>>965で答えが出てるんだが?
968958:03/10/26 00:52
細かく書こうとしたらまた長くなってしまった……。お許しを。

>>964
最初はそのやり方でやっていたんですが、それだと下のようなクラス階層を作るとき

<通常の継承>
クラスB−クラスAB\
クラスA<      クラスD (クラスAが二重に継承される)
クラスC−クラスAC/

クラスABとクラスACの両方がクラスAを継承しているために
ABとACを継承したクラスDでクラスAが二重に継承されてしまいますよね。
969958:03/10/26 00:52
そこで先のように
メンバ関数だけを純粋仮想関数として記述したクラス
AbstactA, AbstractB,AbstractCと、 (以下AbstractをAbsと略します)
AbstractA、AbstractB、AbstractCをそれぞれ仮想継承し、その実体を記述したクラス
ConcreteA,ConcreteB,ConcreteCを別々に作り、 (同様にConcreteをConと略します)
AbsAとAbsBを仮想継承したAbsAB、AbsAとAbsCを仮想継承したAbsACを作って、
クラスDでは、AbsABとAbsACを仮想継承し、ConcAとConcBとConcCを通常の継承で継承する、
という形を採ってやる。

<AbsA,AbsB,AbsCからDまでは全て仮想継承>
クラスAbsB−クラスAbsAB−\
クラスAbsA<           \
クラスAbsC−クラスAbsAC−−−\
                      クラスD (二重に継承されるのは仮想関数名だけ)
                     /|
クラスAbsB−クラスConB−−//|
クラスAbsA−クラスConA−−//
クラスAbsC−クラスConC−−/
<AbsA,AbsB,AbsCからそれぞれのConcまでは仮想継承。ConcからDまでは通常の継承>

こうして二重継承の問題をとりあえず回避できたんですが、
警告も出るしどう見てもスマートな方法ではないかと思ってこちらに書き込んだのです。
>969
何をしたいのか解らないが、
とりあえず、多重継承を使わない設計を考える方が有意義だと思うぞ。
>>968-969
D では AB と AC のどちらの func を呼びたいんだ?
ちゃんと D::func を定義して、
その中でどちらの func を呼ぶか、
または両方呼ぶのかを自分で実装しないとあかんで。
>>968
基底クラスを複数継承してたら、なにか設計が腐ってると思っておけ。

自然にモデリングするとそうなる事もあるんだが、C++ 的には菱形継承は
避けた方が良い。たいていよく考えると代替案が見つかるものだ。

>>970
インターフェースなら複数継承しても、大した害はない。958 は説明が下手すぎて
何をやりたいか推測するしかないんだが、たぶん

1. 抽象基底クラスでインターフェースをいくつか定義 (I1, I2, I3, ...)
2. そのインターフェースに対応する実装を用意 (I1Impl, I2Impl, I3mpl, ...)
3. I1, I2 を継承したコンクリートクラス C12 を作成する際に I1Impl, I2Impl を Mixin 的に
 使いたい。

っつー話なんじゃない? それだと I1Impl, I2Impl をクラステンプレートにするか、
C12 から I1Impl, I2Impl の対応するメソッドにメッセージを転送する関数を
自前で書くのが一般的だが。
973958:03/10/26 01:35
みなさん色々とつきあってくださってありがとうございました。
説明不足のためにだいぶ煩わせてしまったようですみません。
確かにプログラム板で矢印をアロー演算子で書くのは気にしなさすぎでした、申し訳ない。

多重継承を使ったらどういう動きをするかを試しているので
>>970のように言われると元も子もないのですが。
とりあえず自分でもう少し色々やってみようかと思います。
でも基本的に使わずに代替案を探した方がよいというのは肝に銘じて置きます。

>>966で言われた結果は少々気になるので
今動かしているプログラムを挙げられるようにシェイプアップしていたのですが、
これ以上レスを潰すのもアレなので違ったアプローチも含めて色々やってみようかと思います。
974949:03/10/26 01:53
>>956 どうも「.」「..」をスキップする処理をループから出すと動かないので
フォルダ属性がtrueかどうかの判定後に回してみました。早くなったようなならないような…。

void __fastcall TForm1::EnumFolders( const AnsiString DirName , TListItem *Items )
{// ListViewにファイル、フォルダを表示するコールバック関数
 WIN32_FIND_DATA fd; // 列挙情報取得用
 HANDLE hFind; // 検索用のハンドル
 SHFILEINFO fileinfo; //SHGetFileInfo用
 AnsiString Pattern = DirName + "\\*.*"; // ワイルドカードで全ファイル検索
 hFind = FindFirstFile( Pattern.c_str() , &fd );

 if( hFind != INVALID_HANDLE_VALUE ){
  do{ // ファイルが無くなるまで続ける
   if( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){// フォルダ属性か調べる
    if( strcmp( fd.cFileName , "." ) != 0 && strcmp( fd.cFileName , ".." ) != 0 ){ // . と .. は無視する
     AnsiString SubFolder = DirName + "\\" + fd.cFileName; //現在のフォルダに見つかったフォルダを追加
     EnumFolders( SubFolder , Items );// 同じこの関数でサブフォルダを検索する
     (***フォルダとそのアイコンをリストビューに追加する処理***)
    }
   }else{
    (***ファイルとそのアイコンをリストビューに追加する処理***)
   }
  }while( FindNextFile( hFind , &fd ) == true );
 FindClose( hFind );
 }
}
975949:03/10/26 01:56
あとリストビューに追加時にSHGetFileInfoを毎回せずにindex値を記録しとけってことですが、
次のような処理しか思い浮かばなかったんですが、これってスタンダードな方法にも近いものなんでしょうか?

if(構造体の中に拡張子の名前と一緒の変数名があるか){
 その変数が持ってるindex値をファイルに割り当ててリストビューに追加。
}else{
 拡張子と関連づけられたアイコンのインデクス値を::SHGetFileInfoで
 探してファイルに割り当ててリストビューに追加。
 その拡張子名で構造体に登録しとく、値はインデクス値。
}
>969
class D :public ConsA, publib ConsB, public ConsC{..};ですまさないのは、
たぶん、AbsABとかAbsACとかを受けたい別のクラス/メソッドが存在する
からだろうが、俺としてはAbsABを受けるより、素直にAbsA, AbsBの2つを
受けるとか、QueryInterface/dynamic_castを使う方法をおすすめしたい。
>968

class CA
{public: virtual void funcA(){cout << "A";} };
class CB
{public: virtual void funcB(){cout << "B";} };
class CC
{public: virtual void funcC(){cout << "C";} };

class CAB : virtual public CA , public CB{};
class CAC : virtual public CA , public CC{};

class CD : public CAB , public CAC
{
public: virtual void funcA(){cout << "D";}
};

void main(void)
{
CD d;
d.funcA();
d.funcB();
d.funcC();

d.CA::funcA();
d.CAB::funcA();
d.CAC::funcA();
}
>>974
今気がついたが、FindFirstFileに失敗するとhFindが空になるからCloseFileがエラーだぞ。
>「.」「..」の処置
動かんということはないはずだが・・・。ま、今のでいいと思うけど。
でも、どうせ最初に返ってくると判っているので、少し横着をしようと思えば、
if ( FindFirstFile(Pattern.c_str() , &fd)==INVALID_HANDLE_VALUE ) return;
while ( strcmp(fd.cFileName , ".")== 0 || strcmp(fd.cFileName , "..")== 0 ) {
 if ( !FindNextFile(hFind , &fd) ) {
  FindClose(fd);
  return;
 }
}
do {
 ・・・
}
てなこともできる。
>indexの記録
思いついた方法でヲケ。
たとえば、
class IndexMap {
 std::map<AnsiString, int> index_map;
public:
 int operator()( const AnsiString& ext ) {
  if ( index_map.find(ext) == index_map.end() ) {
   //indexの取得
   index_map[ext] = index;
  }
  return index_map[ext];
};
これのインスタンスをFormのメンバにして使う。
まぁ、こうしてもどれだけ速くなるかは判らんけど。
>978
完全にCから離れてWin32APスレの内容だけど、
FindFirstFile(),FindNextFile()のワイルドカード使用時に取り出すファイル名の順番に、
明文化された規則なんてあるの? 確かに文字順に来るみたいだけど……
たいした無駄じゃないし、精神衛生上、判定は共通ループ内のほうがよくね?
980949:03/10/26 05:06
>>978
ありがとうございます。
やはりSHGetFileInfoがスピードのネックになってるのは確かなようなので、
アイコンインデクス値の記録方法は大変参考になります。
自分で全て考えるのに不安はあったので例を書いて頂いて助かりました。

ちなみに何と比べて早いの遅いの言ってたかと申しますと
Flexible Renamer と比べてたのであります。
http://hp.vector.co.jp/authors/VA014830/FlexRena/

ためしに自分のコードのアイコンインデクス値をSHGetFileInfo使わないで、
適当な数字入れて全部同じアイコンにしてもFlexible Renamerの方が早かったです_| ̄|○
しかもこちらのカラムはアイテム名の一個しかないというのに、、、

こちらの方は検索が完了するまでリストビューが描画されないのに、
アチラは描画しながら検索を続けてるというのもありますが(;´Д`)

HACCELってなに?
>>981
俺もそれ気になってた。
983981:03/10/26 12:12
>>982
わかりました。
キーボードアクセラレーターのハンドルの型みたいです。
HACCEL hAc;
hAc=LoadAccleleerators(hInstance,ID);
みたいな感じで使うみたいです。
(プログラマ養成入門講座VisualC++1から)
というか、微妙にスレ違いでした。
オブジェクトとインスタンスって同じようなものですか?
やっぱり微妙にニュアンスが違うだけで
大体同じように使っていいっぽいですね。
「どう違うの」って聞かれたときやっぱり困りそうですが。
クラスオブジェクト
>>295
要求自体にもレベルがあるだろ
1.どんな言語使っても実現が困難なレベル
2.時間と努力を惜しまなければなんとか実現可能なレベル
3.普通にやれば手間がかかるが工夫次第で効率良く書けるレベル
4.工夫しようがないように見えて裏技的な方法でもっと効率良く書けるレベル
普通3だと思うが
誤爆
C++とC++Builder6を初めてまだ1週間程度ですがよろしくお願いします。
エクスプローラからドロップしたテキストファイルを扱うツールを作ろうと思って、
まずドロップされた物がフォルダなのか、テキストファイルなのか、あるいはその他のファイルなのか判定する、
以下のようなコードを書いてみました。(といっても大部分サンプルコードから切り貼りしたものですが)

実際に動作させて見るとテキストファイルをドロップしたときは正しく動作するのですが、
その他のファイルをドロップすると、
プロジェクト Test1.exe が EAccessViolation クラスの例外を生成しました。
'モジュール 'CC3260MT.DLL' のアドレス 32657E39 でアドレス 00000000 に対する読み込み違反がおきました。
というエラーが発生してしまいます。

どのように改善すれば期待通りの動作をしてくれるようになるのでしょうか。
また、とりあえず読み込んでみてエラーが発生するかどうかで、ファイルとフォルダを判定するのは
何かおかしい気がするので、いい方法があれば教えてください。
(コードは次のレス)
//フォームへのドラッグを有効にする
void __fastcall TForm1::FormCreate(TObject *Sender){
DragAcceptFiles(Handle,true);
}

//テキストファイルかそれ以外のファイルかフォルダか判定する
void CheckFileFolder(char FilePath[]){
try{
TFileStream *Stream = new TFileStream(FilePath,fmOpenRead);
delete Stream;
}
//フォルダをファイルとして開くことはできないから読み込みでエラーが発生したらフォルダだと仮定する
catch(EFOpenError &e){
Application->MessageBox("フォルダっぽい","例外");
return;
}
//".txt"がファイル名の末尾に存在するか
if(strcmp(strstr(FilePath,".txt"),".txt") == 0 ){
Application->MessageBox("テキストファイル","strstr");
}
else{
Application->MessageBox("テキストファイルで無い","strstr");
}
}
もう一つ続きます。
//Win32APIからのイベント
void __fastcall TForm1::WMDropFiles(TWMDropFiles & Msg){
int cnt = ::DragQueryFile((HDROP)Msg.Drop, 0xFFFFFFFF,NULL,0);
for(int i=0;i<cnt;i++){
char DragName[MAX_PATH];
::DragQueryFile((HDROP)Msg.Drop, i,DragName,sizeof(DragName));
CheckFileFolder(DragName); //ここでエラーが発生する
}
::DragFinish((HDROP)Msg.Drop);
}

ここまでが関係するコードです。
自作のCheckFileFolder関数を呼び出すところでエラーが発生します。
もろにWin32APIスレの方が適してると思うけど、まあいいか。
ファイル・フォルダの判断に例外なんて使わずに
GetFileAttributes()あたりを使え。
オーバーライドのイメージって
同じペン軸を使って、細い線とか太い線用のペン先につけ変えながら描く感じですか?

オーバーライドしないってのは
ペン軸とペン先がセットになったのを何本も持ってるみたいな。
995
996
997
998
(rァ゚Д゚)rァ 1000ゲッツ!
999
10011001
このスレッドは1000を超えました。
もう書けないので、新しいスレッドを立ててくださいです。。。