I have a class called Control. All other "controls" inherit from this class. Some controls need a default constructor so that I can assign or construct them later.

The classes look like:

class Control
{
    private:
        //All Members Here..
        void Swap(Control &C);

    public:
        Control(DWORD dwExStyle, std::string Class, std::string Title, ....);
        virtual ~Control();

        virtual void Dispose();    
        virtual Control& operator = (Control C);
};

class Button : public Control
{
    public:
        Button();  //Default constructor.. I don't want to constructor a "Control" instance yet..
        Button(std::string Title, Point Location, int Width, int Height, HWND Parent);
        virtual ~Button();

        virtual void Dispose() override;    
        virtual Button& operator = (Button B);
};



Control::~Control() {}

void Control::Swap(Control &C)
{
    //Swap everything else.. and so on..
}

Control::Control(DWORD dwExStyle, std::string Class, std::string Title, ....) : //Initialize stuff..
{
    Handle = CreateWindowEx(dwExStyle, Class.c_str(), Title.c_str(), ...);  //creates a new Control.
}

void Control::Dispose()
{
    ID = nullptr;
    Parent = nullptr;
    DestroyWindow(Handle);
}

Control& Control::operator = (Control C) //Basically Moves.. Steals all information and destructs old object.
{
    if (this->Handle != C.Handle)
    {
        C.Swap(*this);
        C.Dispose();
    }
    return *this;
}


Button::~Button() {}

//The next line is what I want to delay.. I am forced to call the parent constructor..
Button::Button() : Control(...) {}



Button::Button(std::string Title, Point Location, int Width, int Height, HWND Parent) : Create instance of Control.


Button& Button::operator = (Button B)
{
    Control::operator = (B);
    return *this;
}

Examples:

Button* Btn;  //Create a button pointer.. Does not construct or create a window/button.

//WNDPROC......

    WM_CREATE:
        Btn = new Button("Title"...);  //Constructs a button!
    break;

I'm trying to do the above but on the stack instead.. See using a pointer, I'm able to delay the construction of the button later by calling new and constructing it there..
The below does the same thing via the stack except TWO objects are created.

Button Btn;  //This constructs a button object.

//WNDProc....

     WM_CREATE:
        Btn = Button("Title"...);  //Two buttons are now created.. The first is then destructed through assignment..
     break;

I do not want to create two buttons.. However if I do:

WM_CREATE:
    Button Btn = Button("Title"...); //Only one button is created but it is also destroyed when it goes out of scope.
break;

Is there a way I can delay the construction of the button without using a pointer like I did above? I cannot think of a good way to delay the construction of button. Control cannot have a default constructor and if it does I'd have to make it protected and determine which constructor was called so that I can later create the window.

Any ideas?

Recommended Answers

All 2 Replies

There are a few observations that I must make before saying anything else.

First of all, your Control class is a resource-holding class, and should be designed as such. So far, your class is ill-conceived if it is to be holding a resource (i.e., tied to an active window). You have to ask some fundamental questions like: Should the class be copyable? Should the class be moveable? Or neither?

Second, the dynamic polymorphism that you are trying to imbue on the Control/Button classes seems very awkward to me. Seems to me like a clear case of not observing the golden rule of OOP inheritance: "Inherit, not to reuse, but to be reused". I see two telltale signs: (1) your Control class seems to want to serve two purposes, be a base-class with some set of virtual functions and be a provider of some basic functionality in being the care-taker of a window-resource; and, (2) the inheritance is not oriented towards polymorphic services being provided but rather implementation details being shared (e.g., the "Dispose()" function). The main focus of an OOP inheritance scheme should be to establish (through the base classes) a hierarchical set of functionality provided by the descendants (derived classes), if a few innocuous data members (directly tied to functionality) show up in the base classes, it's no big deal, the same goes for a few helper (protected) member functions. But these commonly useful data members or member functions should never be the reason for the inheritance (in OOP code).

For example, you might think "oh, I'm gonna write a bunch of classes that create windows, so I should create a base class that handles a window and inherit from it", that's exactly the wrong way to think about inheritance. This kind of problem mandates composition, not inheritance. The way to reason is this: "oh, I'm gonna write a bunch of classes that create windows, so I should create a small self-contained class that wraps up most of the annoying windows-handling code such that I can reuse it in many different places (as a data member or a local variable)". When you think about it, it makes all the sense in the world, you would never think "oh, I need dynamically allocated memory in this class, so I'm gonna inherit from std::vector". When there's a need to reuse something then go for composition (i.e., a data member), and when there's a desire to be reused for some purpose then inherit from the base class that embodies that purpose by its interface.

Third, you have a virtual assignment operator. This is very odd. A polymorphic class rarely needs an assignment operator (and what goes with it, copy-constructor, swap-op, etc.) because this kind of operation implies value-semantics, i.e., passing the object around by value. Value-semantics isn't really compatible with dynamic polymorphism, or at least, they don't play nice with each other. Value-semantics implies that you know the "final" type of the object, because anything short of that would imply destructive aliasing. Dynamic polymorphism requires an obfuscation of the actual type through the semi-transparent cloak of a reference / pointer. As long as you need dynamic polymorphism, you must pass references / pointers around, and as soon as you need value-semantics, dynamic polymorphism disappears. Then I ask, why would you need a virtual assignment operator? It's an oxymoron.

If what you are looking for is a polymorphic way to copy objects of unknown derived type. What you're looking for is the usual "clone()" function. Usually implemented somewhat like this:

class clonable {
  protected:
    virtual std::unique_ptr<clonable> clone_impl() const = 0;
  public:
    virtual ~clonable() {}
    std::unique_ptr<clonable> clone() const { return clone_impl(); };
};

class Base : public clonable {
  protected:
    virtual std::unique_ptr<clonable> clone_impl() const {
      return std::unique_ptr<clonable>( new Base(*this) );
    };
  public:
    std::unique_ptr<Base> clone() const { 
      return std::unique_ptr<Base>( static_cast<Base*>( clone_impl().release() ) ); 
    };
};

class Derived : public Base {
  protected:
    virtual std::unique_ptr<clonable> clone_impl() const {
      return std::unique_ptr<clonable>( new Derived(*this) );
    };
};

Note the use of the smart-pointer "unique_ptr", a very useful device of C++11.

On a technical note, your implementation of the Button copy-assignment operator:

Button& Button::operator = (Button B)
{
    Control::operator = (B);
    return *this;
}

will lead to destructive slicing of the "B" object which could have unintended consequences depending on the rest of the code. Trying to implement copying behaviour in a class that is part of an class hierarchy (inheritance) is often annoying and filled with little pitfalls like that one (e.g., the usual pass-by-value copy-and-swap idiom doesn't work). This is why resource-holding classes (which require the full suite of special member functions, the "Big Five") should be self-contained, hierarchy-free, value-semantics classes with copy and/or move semantics as necessary. Then, in your OOP classes (with inheritance and dynamic polymorphism) you can make use of RAII classes only, and not have to worry about writing copy-constructors or assignment operators (and moves) since the compiler-generated one is correct, and you, as a programmer, side-step all the potential pitfalls.

Now, to the actual question:

Is there a way I can delay the construction of the button without using a pointer like I did above?

Yes, there are ways, some more dirty than others.

On the dirty end of things, one way is to explicitly call the constructor... yes, you can do that... it's dirty, real dirty, but you can do it with placement-new:

Button Btn;  // Make sure the default constructor does nothing important. (e.g., just sets things to 'null'). NOTE: this still requires Control to have a default constructor, or that Button has something to feed to the Control's constructor.

//WNDProc....

     WM_CREATE:
        new(&Btn) Button("Title"...);  // One button is created on top of the existing (null) one.
     break;

you could also go even further down the dirty road and construct the Button object in memory allocated to a static array of char, e.g., char BtnMem[ sizeof(Button) ];, that would avoid the need for a default constructor.

Of course, the real solution (which you seem to dismiss outright) is to have a default constructor in your Control class such that you can create the Button tentatively, and initialize it later. As in:

Button Btn;  // Create a 'zombie' object.

//WNDProc....

     WM_CREATE:
        Btn.Initialize("Title"...);  // Bring to zombie back to life.
     break;

The above is by far the most common solution, I have yet to see a situation in which you could not realize this easily (and I really don't understand your objections to a default constructor in the base-class, unless it should not be a base-class, as I explained earlier). If you really need to delay the construction of something (as in, literally delay the time that a constructor is called), then you would normally (i.e., without getting dirty) do this with a data member that is a pointer instead of an object. That is:

class Button 
{
    private:
        std::unique_ptr< Control > p_ctrl;
    public:
        Button();  //Default constructor.. leave p_ctrl to point nowhere.
        Button(std::string Title, .....);

        void Initialize(std::string Title, .....);
        void Dispose();
};


Button::Button() : p_ctrl() { };
Button::Button(std::string Title, .....) : p_ctrl() {
  Initialize(Title, .....);
};

void Button::Initialize(std::string Title, .....) {
  p_ctrl = std::unique_ptr<Control>(new Control(Title, .....) );
};

void Button::Dispose() {
  p_ctrl.reset();
};

The above doesn't seem too complicated to do. Also, observe that the use of composition (i.e. the "Control" as a data member, not a base-class) allows for a lot more flexbility (i.e., switching to an internal pointer-based storage, avoiding the delay-of-construction problem altogether).

Another elegant and modern solution is simply to use a move-constructor. If your Button class has a move constructor, then your original solution becomes:

Button Btn;  //This constructs an empty button object.

//WNDProc....

     WM_CREATE:
        Btn = Button("Title"...);  //A new button is "moved" into the empty one,
                                   // here, an empty object is destroyed, overhead is minimal. 
     break;

Also, I doubt that you really want the Button class to be copyable, I think only moveable makes sense here.

commented: Excellent effort and completeness +6

I was trying to emulate C#'s button class.. I went on MSDN and searched button and I noticed they inheritted from a "Control" class so I did what I thought was the implementation that made sense at the time.

I ended up doing the following before reading your solution.. And I used the assignment operator to achieve the movement from WM_CREATE to the button outside that scope. I do not understand how the Cloneable can copy through the this pointer if it doesn't know what to copy? Isn't that what a copy constructor is supposed to do?

I'm still unsure why I should not use inheritance to make the button because I thought about the relationship between the two before designing it and a button "is-a" control.. :S So shouldn't a button inherit from the control class? If I made it a data-member as suggested then it would be: a button "has-a" control. I'm not sure how a button can have other controls unless I'm really going about it all the wrong way but this is the way I learned inheritance in other languages (Java, C#, F#, etc) so I decided to "try" the same thing in C++.

It is my first time doing inheritance in C++ so the relationships of two classes was the first thing that came to mind. Is-A vs. Has-A. Then I thought maybe the Control class can provide a set of functions that all derived classes would also have because when a child inherits from the parent, it gets a set of functionalities from the parent and since a control can move around and stuff, I thought all controls would have the basic functionality from the parent for such things and provide their own implementation for others. Then the second to last thought that came to mind was your tutorial which I'm always reviewing to make sure I don't make mistakes and that's where RAII came in because I thought the control should hold all resources for other controls to use without having to worry about much cleanup. The last thing was to implement all these ideas some how and this is what I came up with.. Which I cannot "see" why it is wrong yet?

class Control
{
    private:
        HMENU ID;
        HWND Handle, Parent;
        std::string Class, Title;
        DWORD dwExStyle, dwStyle;
        Point Location;
        int Width, Height;
        void Swap(Control &C);
        std::array<std::function<void(HWND, UINT, WPARAM, LPARAM)>, 6> Functions;

    public:
        Control(const Control &C) = delete;
        Control(Control &&C);
        Control(DWORD dwExStyle, std::string Class, std::string Title, ...);
        Control(DWORD dwExStyle, std::string Class, std::string Title, DWORD dwStyle, ...);
        virtual ~Control();

        virtual void Dispose();
        virtual void SetText(std::string Text);
        virtual void SetParent(HWND Parent);
        virtual void SetLocation(Point Location);
        virtual void SetSize(int Width, int Height);

        Control& operator = (const Control &C) = delete;
        Control& operator = (Control&& C);

    protected:
        Control();
        bool Initialized;
        void UnInitialized();
        static LRESULT __stdcall SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};

class Button : public Control
{
    public:
        Button();
        Button(const Button &B);
        Button(Button&& B);
        Button(std::string Title, ...);
        Button(std::string Title, ...);
        virtual ~Button();

        virtual void Dispose() override;
        virtual void SetText(std::string Text) override;
        virtual void SetParent(HWND Parent) override;
        virtual void SetLocation(Point Location) override;
        virtual void SetSize(int Width, int Height) override;

        Button& operator = (const Button &B) = delete;
        virtual Button& operator = (Button&& B);
};


Control::~Control() {}

void Control::Swap(Control &C)
{
    using std::swap;
    swap(ID, C.ID);
    swap(Handle, C.Handle);
    swap(Parent, C.Parent);
    swap(Class, C.Class);
    swap(Title, C.Title);
    swap(dwExStyle, C.dwExStyle);
    swap(dwStyle, C.dwStyle);
    swap(Location, C.Location);
    swap(Width, C.Width);
    swap(Height, C.Height);
    swap(Functions, C.Functions);
    swap(Initialized, C.Initialized);
}

void Control::UnInitialized()
{
    if (!Initialized)
    {
        throw std::runtime_error("\nError! Control Not Constructed!");
        MessageBox(nullptr, "Control Not Constructed!", "Initialization Error!", MB_ICONERROR);
        ExitProcess(0);
    }
}

Control::Control(Control &&C) : ID(nullptr), Handle(nullptr), ...
{
    this->Swap(C);
    RemoveWindowSubclass(Handle, SubClass, reinterpret_cast<UINT_PTR>(ID));
    SetWindowSubclass(Handle, SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
    C.Dispose();
}

Control::Control() : ID(nullptr), Handle(nullptr), ... {}

Control::Control(DWORD dwExStyle, std::string Class, std::string Title, ...
{
    static std::size_t IDs = 0;
    ID = reinterpret_cast<HMENU>(++IDs);
    Handle = CreateWindowEx(dwExStyle, Class.c_str(), ...);
    SetWindowSubclass(Handle, SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
}

Control::Control(DWORD dwExStyle, std::string Class, std::string Title, DWORD dwStyle, ...) : ID(ID), Handle(nullptr), ...
{
    Handle = CreateWindowEx(dwExStyle, Class.c_str(), ...);
    SetWindowSubclass(Handle, SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
}

void Control::Dispose()
{
    ID = nullptr;
    Parent = nullptr;

    for (int I = 0; I < 6; ++I)
    {
        Functions[I] = nullptr;
    }

    if (Handle != nullptr)
    {
        DestroyWindow(Handle);
        Handle = nullptr;
    }
}

void Control::SetText(std::string Text)
{
    UnInitialized();
    this->Title = Text;
    SetWindowText(Handle, Text.c_str());
}

void Control::SetParent(HWND Parent)
{
    UnInitialized();
    this->Parent = Parent;
    ::SetParent(Handle, Parent);
    ShowWindow(Handle, SW_SHOW);
}

void Control::SetLocation(Point Location)
{
    UnInitialized();
    this->Location = Location;
    SetWindowPos(Handle, HWND_TOP, Location.X, Location.Y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}

void Control::SetSize(int Width, int Height)
{
    UnInitialized();
    this->Width = Width; this->Height = Height;
    SetWindowPos(Handle, HWND_TOP, 0, 0, Width, Height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}

Control& Control::operator = (Control&& C)
{
    C.Swap(*this);
    RemoveWindowSubclass(Handle, SubClass, reinterpret_cast<UINT_PTR>(ID));
    SetWindowSubclass(Handle, SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
    //C.Dispose();
    return *this;
}

LRESULT __stdcall Control::SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    //GET DYNAMIC ID.. GetWindowLong(HWnd, GWL_ID);
    //GET CLASS NAME.. GetClassName(HWnd, out LPSTR, MaxCount);
    Control* Instance = reinterpret_cast<Control*>(dwRefData);

    switch(Msg)
    {
        case WM_LBUTTONDOWN:
        {
            if (Instance != nullptr && Instance->Functions[0])
                Instance->Functions[0](0, 0, 0, 0);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_NCDESTROY:
        {
            RemoveWindowSubclass(Window, SubClass, uIdSubclass);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        default:
            return DefSubclassProc(Window, Msg, wParam, lParam);
    }
    return 0;
}


Button::~Button() {}

Button::Button() : Control() {};

Button::Button(const Button &B) {}

Button::Button(Button&& B) : Control(std::move(B)) {}

Button::Button(std::string Title, Point Location, ...) : Control(...) {}

Button::Button(std::string Title, ...) : Control(0, "Button",...) {}

void Button::SetText(std::string Text)
{
    Control::SetText(Text);
}

void Button::Dispose()
{
    Control::Dispose();
}

void Button::SetParent(HWND Parent)
{
    Control::SetParent(Parent);
}

void Button::SetLocation(Point Location)
{
    Control::SetLocation(Location);
}

void Button::SetSize(int Width, int Height)
{
    Control::SetSize(Width, Height);
}

Button& Button::operator = (Button&& B)
{
    Control::operator = (std::move(B));
    return *this;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.