Question about cpp classes and objects

Started by
8 comments, last by rip-off 12 years, 10 months ago
2 Quick questions;

Is it possible for a cpp class to stack like so?

WINDOW Window("title");
CHILDWINDOW Window.Child("child title");
BUTTON Window.child.Button("Submit", 10, 10, 100, 20);


And is it possible for an object to access a variable from it's parent?
(the child window needs to access hInstance and hWnd from the main window)

Thank you.
Advertisement
I'm not really sure what you are asking. The code you've posted isn't C++. Are you posting psuedo code? Are they supposed to be declarations or definitions?

And is it possible for an object to access a variable from it's parent?
[/quote]
There is no direct concept of "parent" in C++, unless you are talking about classes, not instances.


(the child window needs to access hInstance and hWnd from the main window)
[/quote]
You could do this:

Window window("title");
Window child = window.createChild("child title");
Button button = child.createButton("Submit", 10, 10, 100, 20);

Or this:

Window window("title")
Window child(window, "child title");
Button button(child, "Submit", 10, 10, 100, 20);

This might require some friendship or accessor methods.
I'm not really sure what you are asking. The code you've posted isn't C++. Are you posting psuedo code? Are they supposed to be declarations or definitions?[/quote]
1 It's pseudo code because I wasn't sure how to write it in cpp. I'm used to php's method of defining a class (though I haven't tried anything like this in php either).
2 they're declarations.

Thank you for your responce, I'll try it right away.

Edit:

ok, so how am I doing? (in otherwords, do you see anything that should have been coded in another way or might cause problems later on?

main.cpp
[source lang="cpp"]#include <windows.h>
#include <tchar.h>

//build this into an error class later
void error(LPCWSTR text){
//MessageBox(NULL, text, "Error", MB_OK | MB_ICONWARNING);
}

#include "window.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

window window("title", hInstance);
Button button = window.createButton("Submit", 10, 10, 100, 20);
//next: add event listener to the button.

//The message loop
MSG msg = {0};
while(msg.message != WM_QUIT){
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}[/source]

window.h
[source lang="cpp"]typedef HWND Button;

class window {
public:
int _iWidth, _iHeight;
HWND _hWnd;
HINSTANCE _hInstance;

window(LPCTSTR sName, HINSTANCE hInstance) {
_iWidth = 800;
_iHeight = 600;
_hInstance = hInstance;

//Fill in the Window class and register it
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = _hInstance;
wcex.hIcon = NULL;
wcex.hIconSm = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = sName;
RegisterClassEx(&wcex);

//Create the window
_hWnd = CreateWindow(sName,
sName,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
_iWidth,
_iHeight,
NULL,
NULL,
_hInstance,
NULL);
if(!_hWnd){
return;
}

}
//////////////////////////////////////////////////////////
// Create Button
//
Button createButton(LPCTSTR text, int iX, int iY, int iWidth, int iHeight) {
return CreateWindow("BUTTON",
text,
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
iX,
iY,
iWidth,
iHeight,
_hWnd,
NULL,
_hInstance,
NULL);
}
//////////////////////////////////////////////////////////
// Window Procedure
//
private:
static LRESULT CALLBACK WndProc(HWND _hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch(uMsg){
case WM_DESTROY:
PostQuitMessage(0);
break;
}

return DefWindowProc(_hWnd, uMsg, wParam, lParam);
}
};[/source]
Looks reasonable to me, but I'm not a Win32 programmer.

Here are some things I did notice:


  • Consider using typedef rather than #define for BUTTON though.

  • Type names are generally written in CamelCase. ALL_CAPS is mostly reserved for constants and preprocessor defines such as include guards.

  • Many people consider hungarian notation to be pointless and verbose, but that is just a stylistic issue.

  • You have little or no error checking. Consider exceptions for errors during object construction.

  • I think your WNDCLASSEX strucure should be zero initialised for safety.
Thank you

- Consider using typedef rather than #define for BUTTON though.
Sorted

- Type names are generally written in CamelCase. ALL_CAPS is mostly reserved for constants and preprocessor defines such as include guards.
Sorted, but why are all the win32 types in upper case?

- Many people consider hungarian notation to be pointless and verbose, but that is just a stylistic issue.
It worked fine on all my php projects. I like the hungarian notation because I distinguish variables from definitions better. (visual studio doesn't highlight them)

- You have little or no error checking. Consider exceptions for errors during object construction.
Yea, I am going to do that after I finish this class. I'm planning on making a custom window template for it, since I don't like the standard MessageBox() function.

Sorted, but why are all the win32 types in upper case?
[/quote]
Win32 is a very old API. Things were differn't in them days. If you see how Microsoft designed C# they moved away since.


It worked fine on all my php projects. I like the hungarian notation because I distinguish variables from definitions better. (visual studio doesn't highlight them)
[/quote]
In dynamic languages it makes more sense because there is no static type checker, nor one definitive place you can go to find the type of a variable. Its a minor stylistic issue, don't sweat it if you prefer it. You might find this an interesting read though.
Something screwed up and removed my reply to your last

I think your WNDCLASSEX strucure should be zero initialised for safety.
Like this right? WNDCLASSEX wcex = {0};
Why should I zero initialise it? the variable is local (right?), it gets destroyed after the class [s]initiator[/s] constructor ends.

In dynamic languages it makes more sense because there is no static type checker, nor one definitive place you can go to find the type of a variable. Its a minor stylistic issue, don't sweat it if you prefer it. You might find this an interesting read though.[/quote]
Makes sence, since the compiler will tell me if I have a type mismatch. It's better to note which kind of variable it is instead of which type.

One thing though. What do you think of this line?

_hInstance = hInstance; (line 12 of windows.h)

Why should I zero initialise it? the variable is local (right?), it gets destroyed after the class initiator ends.
[/quote]
These two things are unrelated. The variable is local. It will be deallocated at the end of the constructor.

C APIs are often written such that zero-initialised data is considered a "safe default". Most of the Win32 code I have seen uses zero initialisation on all structures before filling the relevant fields and passing them to the function. Regardless, setting values to some constant is going to be easier to debug than than uninitialised memory which can change between invocations of the program.

I've since looked at the WNDCLASSEX documentation and you do appear to fill in all the fields, so it is not a big deal.
In your static WndProc, once you start to respond to other messages, you have no way to access the window class instance, so I am a bit unclear on what the purpose of this wrapper is, other than to create a window and buttons.

An approach you might like to consider is this (I'll use my own conventions for naming as I agree with rip-offs comments):

[source lang="cpp"]
class Window
{
private:
HWND hw;
HINSTANCE hIn;

static HRESULT WINAPI WndProc(HWND hw,UINT msg,WPARAM wParam,LPARAM lParam);

public:
Window(/*...*/);
~Window();

void OnSize(){ /* just an example */ }
void OnLButtonDown(int x,int y){ }
};


Window::Window(/*...*/)
{
// create the window as you are
// although note you probably only want to
// register the window class once so either
// separate into another static method or use
// a first-time flag

SetWindowLongPtr(hw,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(this));
}


Window::~Window()
{
SetWindowLongPtr(hw,GWLP_USERDATA,NULL);
DestroyWindow(hw);
}

HRESULT WINAPI Window::WndProc(HWND hw,UINT msg,WPARAM wParam,LPARAM lPARAM)
{
Window *w=reinterpret_cast<Window*>(GetWindowLongPtr(hw,GWLP_USERDATA));
if(!w) return DefWindowProc(hw,msg,wParam,lParam);

switch(msg)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_SIZE : w->OnSize(); return 0;

case WM_LBUTTONDOWN: w->OnLButtonDown(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)); return 0; // need windowsx.h for this macro
}

return DefWindowProc(hw,msg,wParam,lParam);
}
[/source]

In essence, store a pointer to the (C++ class) Window instance in the userdata section of the (Windows kernel) window instance, retrieve it in the WndProc, do any parameter decoding there then pass the message along to a more strongly-typed method called upon the instance associated with the HWND passed into the WndProc.

There was an article on the old site that used a more complex approach of passing the instance in via CreateWindow and responding to the WM_NCCREATE message but I've found the above to work just as well as long as you don't need your instance during the flurry of messages that get sent during CreateWindow's execution.

One caveat - if you try this approach with a Window hierarchy that uses multiple inheritance, all bets are off and it becomes undefined as it its unsafe to reinterpret_cast<> a pointer to such a class. I think. Or is that only when using virtual inheritance?

Brief opinion: The original intent of hungarian was to express the type of a variable in terms of its function, not in terms of its static type e.g. int colX,colY in a spreadsheet or float posX,posY in a CAD application. The bastardized version that evolved to encode its static type is a maintenance nightmare and avails you very little, even in an IDE that does not highlight the type. I'd seriously suggest you reconsider your naming convention.

One caveat - if you try this approach with a Window hierarchy that uses multiple inheritance, all bets are off and it becomes undefined as it its unsafe to reinterpret_cast<> a pointer to such a class. I think. Or is that only when using virtual inheritance?
[/quote]
I believe the requirement is that if you store a pointer in a void * you may only cast it back to the original type you stored in it. So since the type of "this" is Window * inside the constructor and you are reinterpret_cast<>ing it back to a Window pointer it should be safe regardless of multiple or virtual inheritance.

This topic is closed to new replies.

Advertisement