配列とVECTOR に渡した値の違いについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ドラきち
記事: 10
登録日時: 5年前

配列とVECTOR に渡した値の違いについて

#1

投稿記事 by ドラきち » 5年前

※当初投稿したコードに誤りがあり、No.07 へ改めて 質問とコードを投稿させていただきました。

件名について、質問させてください。
以下コードにおいて、私は以下の結果を想定していました。
hDCtest = cTest01.TestArray[0].hDC = cTest01.itr->.hDC

しかし、実際は以下の様に動作しているようです。
hDCtest = cTest01.TestArray[0].hDC ≠ cTest01.itr->.hDC

vectorを介したことで、クラスBの要素 HDCに何か変化があるのでしょうか?
ご教示いただけますよう、よろしくお願いいたします。

コード:

class A {
	HDC hDC
};

class B {
	A TestArray[10];
	vector<TEST_A> TestVect;
	vector<TEST_A>::iterator itr;
};


main() {
	B cTest01;
	HDC hDCtest = (なんらかのHDCを返却する関数)

	//クラス配列0番目 の要素に代入
	cTest01.TestArray[0].hDC = hDCtest;

	//ベクタに追加し参照

	A cTest02;
	cTest02.hDC = hDCtest;
	cTest01.TestVect.push_back(cTest02);
	cTest01.itr = cTest.TestVect.begin();
	cTest01.itr->hDC
}
開発環境:VC++2005
動作環境:Wndows7
最後に編集したユーザー ドラきち on 2015年3月30日(月) 20:02 [ 編集 4 回目 ]

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: 配列とVECTOR に渡した値の違いについて

#2

投稿記事 by h2so5 » 5年前

21行目で存在しないメンバにアクセスしているようですが。

ドラきち
記事: 10
登録日時: 5年前

Re: 配列とVECTOR に渡した値の違いについて

#3

投稿記事 by ドラきち » 5年前

>h2so5 様

返答ありがとうございます。
コードに誤りがありましたので、修正させていただきました。
申し訳ございません。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: 配列とVECTOR に渡した値の違いについて

#4

投稿記事 by h2so5 » 5年前

23行目のcTestの宣言はどこですか?

ドラきち
記事: 10
登録日時: 5年前

Re: 配列とVECTOR に渡した値の違いについて

#5

投稿記事 by ドラきち » 5年前

>h2so5 様

度々申し訳ございません。
23~25行目の cTest は cTest01の誤りでした。 修正いたしました。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: 配列とVECTOR に渡した値の違いについて

#6

投稿記事 by h2so5 » 5年前

まだ間違っています。
中途半端に抽出した擬似コードではなく、実際にコンパイル可能なコードを提示してもらわないと状況が分かりません。

ドラきち
記事: 10
登録日時: 5年前

Re: 配列とVECTOR に渡した値の違いについて

#7

投稿記事 by ドラきち » 5年前

>h2so5 様

大変失礼いたしました。動作するコードを用意いたしました。
※動作のためにはCドライブ直下にBMPファイル(test.bmp)を置く必要がります。

動作させますと黒い四角が描画されるだけのものですが、149行目をコメントアウトし、
150行目を有効とすると、Cドライブ直下に用意したBMPが表示されます。


149行目は、CImageManagerクラス内で、CImageクラスを操作し画像表示を試みています。
150行目は、CImageクラスをダイレクトに操作し、画像表示を試みています。

成功と失敗の動作を比較して、CImageManagerクラス内ではvectorを介してCImageクラス
の操作をしているという違いがあり、そこが原因と考え最初の質問を投稿させていただきました。

コード:

#include<windows.h>
#include<vector>

#define WIDTH	230
#define HEIGHT	350

using namespace std;

//--------------------------------------------------------------------
// クラス定義
//--------------------------------------------------------------------

class CImage {

public:
	CImage(){ Width = 189; Height = 150; }
	~CImage() { DeleteDC(memoryDC); DeleteObject(hBmp); }
	int getWidth(){	return Width;}
	int getHeight(){ return Height;}
	HDC getMemDC(){ return memoryDC;};
	void loadBmpFile(char *, HINSTANCE, HWND);

private:
	char * Name;
	int Width;
	int Height;
	HDC hDC;
	HANDLE hBmp;		// ビットマップハンドル
	HDC memoryDC;
};

class CImageManager {

public:

	int ImgCount;		// CIMGクラス登録数
	int SelectNumber;	// 選択注のIMG番号
	HINSTANCE hInst;	// メインウインドウのインスタンスを保持する
	HWND hDlgWnd;		// 親ウインドウ(ダイアログ)のウインドウハンドル

	void Initialize(HINSTANCE,HWND);	//	情報の初期化
	void addImage(char * filename);
	void write(HDC);

private:
	vector<CImage> Image;
	vector<CImage>::iterator itrImage;		// CImageクラスLISTのイテレータ
	CImage ImageArr[10];
};

void CImage::loadBmpFile(char *filename, HINSTANCE hInst, HWND hDlgWnd) {

	hDC = GetDC(hDlgWnd);
	hBmp = LoadImage(
		hInst,
		filename,//このファイルを読み込む
		IMAGE_BITMAP,
		0,
		0,
		LR_CREATEDIBSECTION | LR_LOADFROMFILE
	);
	memoryDC = CreateCompatibleDC(hDC);
	SelectObject(memoryDC, hBmp);

	ReleaseDC(hDlgWnd, hDC);
}

void CImageManager::Initialize(HINSTANCE i_hInst, HWND i_hWnd){
	hInst = i_hInst;
	hDlgWnd = i_hWnd;
}

void CImageManager::addImage(char * filename){

	CImage obj;

	obj.loadBmpFile(filename, hInst, hDlgWnd);

	Image.push_back(obj);
	itrImage = Image.begin();

}

void CImageManager::write(HDC destDC) {

	itrImage = Image.begin();

	BitBlt(destDC, 0, 0, itrImage->getWidth(), itrImage->getHeight(), itrImage->getMemDC(), 0, 0, SRCCOPY);

}

//--------------------------------------------------------------------
// グローバル変数宣言
//--------------------------------------------------------------------

CImageManager cImgMng;
CImage cImg;

HINSTANCE hInst;
char * showFileOpenDialog(HWND);


LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static HBITMAP hBitmap;
	static HDC hMemDC;
	static BITMAPINFO bmpInfo;
    static LPDWORD lpPixel;

	switch(uMsg) {

		case WM_CREATE:

			//DIBの情報を設定する
			bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
			bmpInfo.bmiHeader.biWidth=WIDTH;
			bmpInfo.bmiHeader.biHeight=HEIGHT;
			bmpInfo.bmiHeader.biPlanes=1;
			bmpInfo.bmiHeader.biBitCount=32;
			bmpInfo.bmiHeader.biCompression=BI_RGB;

			//DIBSection作成
			hdc=GetDC(hWnd);
			hBitmap=CreateDIBSection(hdc,&bmpInfo,DIB_RGB_COLORS,(void**)&lpPixel,NULL,0);
			hMemDC=CreateCompatibleDC(hdc);
			SelectObject(hMemDC,hBitmap);
			ReleaseDC(hWnd,hdc);
			
			cImgMng.Initialize(hInst, hWnd);

			cImgMng.addImage("c:\\test.bmp");

			cImg.loadBmpFile("c:\\test.bmp",hInst, hWnd);

			SetTimer(hWnd,1,100,NULL);

			return 0;

		case WM_TIMER:
			InvalidateRect(hWnd,NULL,FALSE);
			return 0;

		case WM_PAINT:
			SelectObject(hMemDC,GetStockObject(BLACK_BRUSH));
			Rectangle(hMemDC,0,0,WIDTH,HEIGHT);

			cImgMng.write(hMemDC);
		  //BitBlt(hMemDC,0,0,WIDTH,HEIGHT,cImg.getMemDC(),0,0,SRCCOPY);
			
			hdc = BeginPaint(hWnd,&ps);

			BitBlt(hdc,0,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);
			EndPaint(hWnd,&ps);

			return 0;

		case WM_DESTROY:
			KillTimer(hWnd,1);
			DeleteDC(hMemDC);
			DeleteObject(hBitmap);
			PostQuitMessage(NULL);
	}
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}


int WINAPI WinMain(
	HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR lpCmdLine,int nCmdShow)
{
	WNDCLASS wc;
	MSG msg;
    hInst = hInstance;    //グローバル変数にコピー

	wc.style		 = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc	 = WindowProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = LoadIcon(NULL,IDI_APPLICATION);
	wc.hCursor		 = LoadCursor(NULL,IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName	 = (LPCSTR)"";
	wc.lpszClassName = __FILE__;
	if(!RegisterClass(&wc)) return 0;

	HWND hWnd=CreateWindow(
		__FILE__,"ex",
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		CW_USEDEFAULT,CW_USEDEFAULT,
		320,240, 
		NULL,NULL,hInstance,NULL);
	if(hWnd==NULL) return 0;

	BOOL bRet;
	while((bRet=GetMessage(&msg,NULL,0,0))!=0){
		if(bRet==-1) break;
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: 配列とVECTOR に渡した値の違いについて

#8

投稿記事 by YuO » 5年前

ドラきち さんが書きました:成功と失敗の動作を比較して、CImageManagerクラス内ではvectorを介してCImageクラス
の操作をしているという違いがあり、そこが原因と考え最初の質問を投稿させていただきました。
ざっとコードを見ましたが,CImageはコピーされることに対してまったく対処がなされていません。
しかし,std::vector::push_backはオブジェクトをコピーします。
このため,CImageManager::addImageでは,
  1. obj.loadBmpFileの呼び出しでobj.hBmpとobj.memoryDCが設定される
  2. Image.push_back(obj)によって,objのビット単位のコピーがImageに複製される
  3. 関数から抜けるときに,objのデストラクタが走り,obj.hBmpとobj.memoryDCが破棄される。
    この時,同じハンドル値をコピーしただけであったImage.back().hBmpやobj.back().memoryDCは,無効なオブジェクトへのハンドルを保持し続ける
という動作になります。
無効なオブジェクトへのハンドルをbitbltに渡しているのですから,正しく描画できないのも当然となります。


というか,失敗としているときのbitbltの戻り値は0だったのではないですか。
そこでGetLastErrorを呼び出せば,もっと情報が得られていたのではないでしょうか。

ドラきち
記事: 10
登録日時: 5年前

Re: 配列とVECTOR に渡した値の違いについて

#9

投稿記事 by ドラきち » 5年前

>YuO 様
ご返答いただきありがとうございます。
昨日から自分でも色々と試しておりましたが、解決には至っていませんでした。
返答いただいた内容を読み、以下の対応を取りました。
YuO さんが書きました:ざっとコードを見ましたが,CImageはコピーされることに対してまったく対処がなされていません。
クラスのコピーをキーワードに調査し、CImageにコピーコンストラクタを追加しました。
しかし、LoadImage関数で読み込んだ内容をコピーする方法がわからなかったため、
読み込みファイル名のパスを記憶しておき、コピーコンストラクタの中で再度読み込む方法をとりました。

とりあえず問題は解決しましたが、書き方がスマートでありません。
下記のような方法ではなく、loadImage関数で読み込んだ内容をコピーする方法はあるのでしょうか?

コード:

CImage::CImage(const CImage& obj){
	this->filename = obj.filename;
	this->Width = obj.Width;
	this->Height = obj.Height;
	this->hBmp = LoadImage(
        hInst,
		this->filename ,
        IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE
    );

    hDC = GetDC(hDlgWnd);
    this->memoryDC = CreateCompatibleDC(hDC);
	SelectObject(this->memoryDC, this->hBmp);
    ReleaseDC(hDlgWnd, hDC);
}

アバター
usao
記事: 1573
登録日時: 6年前

Re: 配列とVECTOR に渡した値の違いについて

#10

投稿記事 by usao » 5年前

使ったことは無いですが,CopyImage()というAPIがあるみたいですよ.
https://msdn.microsoft.com/en-us/librar ... 85%29.aspx

(ただ, 「CImageをコピーすること」自体が本当に必要なことなのかどうか がやや疑問ではありますが.)

ドラきち
記事: 10
登録日時: 5年前

Re: 配列とVECTOR に渡した値の違いについて

#11

投稿記事 by ドラきち » 5年前

>usao 様
ご返答いただきありがとうございます。
CopyImage関数について調べてみます。
usao さんが書きました:(ただ, 「CImageをコピーすること」自体が本当に必要なことなのかどうか がやや疑問ではありますが.)
これは「本来コピーせずとも解決できるのにあさっての方向へ向かっている」という意味と捉えればよろしいですか?

読み込んだビットマップファイルを順次vectorに詰め込んでおき、n番目のビットマップから任意の範囲を画面へ転送~
というのが私のやりたいことです。 追加されるファイ個数が可変であることからこのような手法を考えてみました。
より良い手法がありましたらご教示いただきたいと思います。

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: 配列とVECTOR に渡した値の違いについて

#12

投稿記事 by YuO » 5年前

ドラきち さんが書きました:クラスのコピーをキーワードに調査し、CImageにコピーコンストラクタを追加しました。
しかし、LoadImage関数で読み込んだ内容をコピーする方法がわからなかったため、
読み込みファイル名のパスを記憶しておき、コピーコンストラクタの中で再度読み込む方法をとりました。
今回のような場合だと,CImage自身が持っている情報をCImageのコピー時に共有してしまえばよいため,hBmpなどを複製する必要はありません。
ただし,共有数をちゃんとカウントし,共有数が0になった時点で破棄する必要があります。

このような場合,std::shared_ptrを使うことで楽をすることができます。
CImageが持っている共有する情報をCImageImplのようなクラスに持たせ,CImageはstd::shared_ptr<CImageImpl>を持つようにします。
こうすれば,CImageをコピーしてもCImageImplの実体はコピーされなくなり,共有数が0になったタイミングでCImageImplのデストラクタが走るため,ちゃんと破棄も行えます。

とりあえず,以下は思いつきで書いただけのコードです。
コンパイル等を行っていないため,各種コンパイルエラーやバグが残っていて動作しない可能性が大いにあります。

コード:

#include <memory>
#include <windows.h>

class CImage {
private:

    class Impl {
    private:
        int Width;
        int Height;
        ::HBITMAP hBmp;
        ::HDC memoryDC;

        // 安全のために,コピーコンストラクタ等は無効化しておく
        Impl (const Impl &) = delete;
        Impl & operator = (const Impl &) = delete;
        Impl (Impl &&) = delete;
        Impl & operator = (Impl &&) = delete;
    public:
        Impl () : Width(189), Height(150), hBmp(nullptr), memoryDC(nullptr) { }
        ~Impl ()
        {
            if (memoryDC) ::DeleteDC(memoryDC);
            if (hBmp) ::DeleteObject(hbmp);
        }

        int getWidth () const { return Width; }
        int getHeight () const { return Height; }

        void loadBmpFile (const char *, ::HINSTANCE, ::HWND);
        bool draw (::HDC destDC) const;
    };

    std::shared_ptr<Impl> impl_;

public:
    CImage () : impl_(new Impl()) { }

    int getWidth () const { return impl_->getWidth(); }
    int getHeight () const { return impl_->getHeight(); }
    void loadBmpFile (const char *filename, ::HINSTANCE hInst, ::HWND hDlgWnd) { impl_->loadBmpFile(filename, hInst, hDlgWnd); }
    bool draw (::HDC destDC) const { return impl_->draw(destDC); } // 描画関数自体を内包させた
};

// この関数は,元の関数をほぼコピーしただけの物。
void CImage::Impl::loadBmpFile (const char *filename, ::HINSTANCE hInst, ::HWND hDlgWnd)
{
    auto hDC = ::GetDC(hDlgWnd); // hDCはメンバ変数である必要が無いので自動変数にした
    hBmp = static_cast<HBITMAP>(::LoadImageA(
        hInst,
        filename,
        IMAGE_BITMAP,
        0,
        0,
        LR_CREATEDIBSECTION | LR_LOADFROMFILE
    ));
    memoryDC = ::CreateCompatibleDC(hDC);
    ::SelectObject(memoryDC, hBmp);
 
    ::ReleaseDC(hDlgWnd, hDC);
}

bool CImage::Impl::draw (::HDC destDC) const
{
    return ::BitBlt(destDC, 0, 0, Width, Height, memoryDC, 0, 0, SRCCOPY) != FALSE;
}
CImageの操作は全てCImage::Implに委譲して,CImage::Implが全てを行うようにしています。
HDCを外に見せる必要が無さそうなので,それも見せないようにしています。

アバター
usao
記事: 1573
登録日時: 6年前

Re: 配列とVECTOR に渡した値の違いについて

#13

投稿記事 by usao » 5年前

>これは「本来コピーせずとも解決できるのにあさっての方向へ向かっている」という意味と捉えればよろしいですか?

CImageのコピーが必要なのかどうか正確なところはこちらにはわからないわけですが,
例えば,
vector< CImage > じゃなくて vector< CImage* > にしとけば CImageManager::addImage() では
CImageのコピーをしなくても良いかもしれない.

コード:

void CImageManager::addImage(char * filename){
 
    CImage *pNewObj = new CImage;  //※deleteはCImageManagerのデストラクタとかでやることにするとして.
    pNewObj->loadBmpFile(filename, hInst, hDlgWnd);  //※←これ,ロード成功したかどうかくらいは返した方がいいんじゃ…?
 
  //※Imageはvector< CImage* >型
    Image.push_back( pNewObj );

    itrImage = Image.begin();
}

閉鎖

“C言語何でも質問掲示板” へ戻る