static member functions(valid use of?)

Dec 11, 2011 at 10:26am
I had an idea for wrapping the general creation of a window into a single class using static member functions and a single static member variable. After thinking about it for a while, I realized I had never really used the static keyword that much and wanted to know when and where it should be used and when it would be considered overkill. Well, after spending lots of time online and looking through a few books, I found very little information about what I wanted to know. So I just put together an example of how I would like to use static member functions in my class and would like some feedback on weather or not this is a practical use of static member functions, or if I am overdoing it, or breaking any coding rules, better way of doing it, ect.

Since the Window class has a static Window object within itself and the constructor is private, this guarantees that only one instance of the object ever exists and is also immediately and automatically created before any of the static member functions can be called, at least I think this is the case. Anyways, here is the code:

window.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <Windows.h>
#include <string>

namespace Engine3D
{
class Window
{
  private: HWND handle_;
	   WNDCLASS wc_;
	   MSG message_;
	   static Window window_;
	   Window();
  public:  ~Window();
	   static void SetWindowTitle(std::string _title);
	   static void SetDimensions(UINT _width, UINT _height);
	   static void Update();
};
}


window.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "window.h"

namespace Engine3D
{
Window Window::window_;

LRESULT CALLBACK WndProc(HWND _hwnd, UINT _msg, WPARAM _wParam, LPARAM _lParam)
{
  switch(_msg)
  {
     case WM_CLOSE:   DestroyWindow(_hwnd);
		      break;
     case WM_DESTROY: PostQuitMessage(0);
		      break;
     default:         return DefWindowProc(_hwnd, _msg, _wParam, _lParam);
  }
  return 0;
}

Window::Window()
{
  ZeroMemory(&handle_, sizeof(HWND));
  ZeroMemory(&wc_, sizeof(WNDCLASSEX));
  ZeroMemory(&message_, sizeof(MSG));

  wc_.style = CS_HREDRAW | CS_VREDRAW;
  wc_.lpfnWndProc = WndProc;
  wc_.cbClsExtra = 0L;
  wc_.cbWndExtra = 0L;
  wc_.hInstance = GetModuleHandle(NULL);
  wc_.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc_.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc_.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
  wc_.lpszMenuName = NULL;
  wc_.lpszClassName = "window";

  if(!RegisterClass(&wc_))
  {
	MessageBox(NULL, "Unable To Register Window.", "ERROR", MB_OK | MB_ICONERROR);
	PostQuitMessage(0);
	return;
  }

  //CreateWindow() sets the Window area size, we want to set the
  //Client area size, this is done by using AdjustWindowRectEx().
  RECT wr = {0, 0, 800, 600};                                             // set the size
  AdjustWindowRectEx(&wr, WS_OVERLAPPEDWINDOW, false, WS_EX_CLIENTEDGE);  // adjust the size

  handle_ = CreateWindowEx( WS_EX_CLIENTEDGE,
	                    "window",
	                    "Window",
			    WS_OVERLAPPEDWINDOW,
			    CW_USEDEFAULT,
			    CW_USEDEFAULT,
			    wr.right - wr.left,  //height
			    wr.bottom - wr.top,  //width
			    NULL,
			    NULL,
			    wc_.hInstance,
			    0);

  if(!handle_)
  {
	MessageBox(NULL, "Error Creating Window.", "ERROR", MB_OK | MB_ICONERROR);
	PostQuitMessage(0);
	return;
  }

  ShowWindow(handle_, SW_SHOW); //make window visible
  UpdateWindow(handle_);        //draw the window
}

Window::~Window()
{
  UnregisterClass(wc_.lpszClassName, wc_.hInstance); //unregister window
  DestroyWindow(window_.handle_);                    //destroy the window
}

void Window::Update()
{
  //loop through windows messages
  while(PeekMessage(&window_.message_, NULL, 0, 0, PM_REMOVE))
  {
    TranslateMessage(&window_.message_);
    DispatchMessage(&window_.message_);
  }
}

void Window::SetWindowTitle(std::string _title)
{
  SetWindowText(window_.handle_, _title.c_str());
}

void Window::SetDimensions(UINT _width, UINT _height)
{
  //Client Rectangle - Area the programmer can draw on
  //Window Rectangle - Area including the menu, title bar, borders, ect.
  
  //Get current client and window rectangle
  RECT rcClient, rcWindow;
  GetClientRect(window_.handle_, &rcClient);
  GetWindowRect(window_.handle_, &rcWindow);

  //adjust width and height accordingly
  int width = (rcWindow.right - rcWindow.left) - rcClient.right + _width;
  int height = (rcWindow.bottom - rcWindow.top) - rcClient.bottom + _height;
  
  //resize the client area
  MoveWindow(window_.handle_, rcWindow.left, rcWindow.top, width, height, true);
}
}


WinMain.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "window.h"

INT WINAPI WinMain(HINSTANCE _cI, HINSTANCE _pI, PSTR _cmd, int _show)
{
  Engine3D::Window::SetWindowTitle("Hello!");
  Engine3D::Window::SetDimensions(640, 480);

  while(!(GetAsyncKeyState(VK_ESCAPE) & 0x8000))
  {
	Engine3D::Window::Update();
  }
  return 0;
}



Last edited on Dec 11, 2011 at 10:27am
Dec 11, 2011 at 10:56am
It depends. An example from me - I have a class for 2D vectors. For 2D Vectors there are 2 kinds of products defined, the dot product for one, and the multiplication with a scalar.
ex
[1,2] * [4,5] = 14
3*[1,2] = [3,6] 


Now I could overload the * operator for both operations, but I decided that would make code portions in which I use both hard to read, so instead I made the dot product a static function (I could have made it a member function, but I thought it looked weird):
 
static double dot(const Vector2D&, const Vector2D&);


Other than that, you also use static members for type-related stuff that isn't associated with an instance of the type, for example for the named constructor idiom (aka static factory method)
static MyLevel loadFromFile(const std::string&);
looks nicer than
MyLevel(const std::string&);.
Dec 12, 2011 at 10:23pm
I'm not sure about the mixture you have there.

Static member functions are just like global functions except with a scoped name (Window::Update() etc)

You can call a static function even before any instances of the object have been created.

So I don't think you can access the member variables (handle, wc_ etc) from within the static function (they have no access to a 'this' pointer)

One option, which I have used, is to have all data static, and all member function non static (including a constructor, which is empty) (weird, yeah)
This is known as the 'monostate pattern' http://c2.com/cgi/wiki?MonostatePattern

When you create a new object, its not really new, since it points to the same static data as all the other Window objects. Its really just a way of accessing the static data.
Dec 12, 2011 at 10:52pm
And don't forget the famous singleton pattern
Dec 14, 2011 at 1:16pm
Thanks for the replies. Never heard of singletons till now. After reading more about singletons I've determined to abandon my original design and re-architect my project.
Topic archived. No new replies allowed.