クラスまみれのゲームプログラミング入門

10:友達なら当たり前

 さて、WinMain関数の中に先ほど作成したCGameObjectを作成し、Initialize()を実行させるわけなのですが、すべてのプログラムで1回きりしか使わない関数をpublicにするのはあまり意味はなく、派生クラスからもアクセスできてしまうのもなんだか不格好です。かといってprivateではアクセスできない。そんなときに便利なのがフレンド関数です。

 フレンド関数はその名が示すように、ここで宣言された関数をクラスのお友達と見なし、クラス内のprivateな情報もフレンド関数から直接アクセスできるようになるのです。

 ためしにCGameObjectの宣言部分をこう変えてみましょう。

GameObject3.h
#pragma once

#include <tchar.h>
#include <windows.h>

#include <dxerr9.h>
#include <d3d9.h>
#include <d3dx9.h>

#define RELEASE(x) {if(x) x->Release();}

class CGameObject
{
private:
	static HWND hWnd;
	static HINSTANCE hInstance;
	
	BOOL Initialize(HWND hWnd, HINSTANCE hInstance);
	void Uninitialize();

	friend int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int);

public:
	static const HWND GetHWnd(){ return hWnd; }
	static const HINSTANCE GetHInstance(){ return hInstance; }

protected:
	static LPDIRECT3D9 pD3D;
	static LPDIRECT3DDEVICE9 pD3Ddevice;
	static LPD3DXSPRITE pSprite;
};

 Initialize()とUninitialize()をprivateに、WinMain()宣言をfriendとしてクラス内に登録しました。この結果、

objectchild.h
class CGameObjectChild : public CGameObject
{
	void FooUninitialize()
	{
		Uninitialize();
	}
};

 このように派生クラスからUninitialize()を呼び出しても、「privateなメンバにアクセスできません」と、コンパイルエラーになりますが、

winmain.cpp
int APIENTRY _tWinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpszCmdLine, int nCmdShow)
{

	/* 省略 */

	HWND hWnd = CreateWindow(
		wc.lpszClassName,
		_T("クラスまみれのゲームプログラミング入門"),
		WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,	
		NULL, NULL, hInstance, NULL );

	CGameObject game;
	game.Initialize(hWnd, hInstance);

	/* 省略 */

	game.Uninitialize();

	return 0;
}

 お友達の関数であるWinMain()内でInitialize()を呼び出しても、コンパイルエラーとならず、すんなりと実行ファイルが完成してしまいます。

 フレンドクラスなるものもC++には用意されており、こちらはお友達関係のクラスであれば、宣言しているクラスのprivateな情報であろうが、自由にいじくり回すことができ、このように使用できる場所を限定させることで、明確なプログラミングができるようになります。

 ただし、フレンド宣言を多用するとprivateの意味ないじゃん。てなことになりかねませんので、ご利用は計画的に。

sample.cpp
// フレンドクラスのデモプログラム

#include <iostream>
using namespace std;

class Class2;	// あらかじめの宣言

class Class1
{
private:
	friend Class2;	// Class2をお友達にすることを宣言
	void Kansu()
	{
		cout << "privateな関数です" << endl;
	}
};

class Class2
{
public:
	void Kansu2()
	{
		Class1 c1;
		c1.Kansu();	// Class1のプライベートな関数にアクセス
	}
};

void main()
{
	Class2 c2;
	c2.Kansu2();
}