5

I can't seem to find the answer but maybe I'm searching the wrong terminology. I am not finding the answer I am looking for in the hits.

I have a bunch of derived classes for a menu system.

I have a CControl derived class that is parent of a CEditBox and a CLabel class. CLabel is nothing more than attaching the text onto an SDL_Surface and then binding it to a texture for openGL to render. CEditBox would be a field for displaying text or gathering text from users like a password box. Obviously, CEditBox could make use of a label for handling the text rendering inside the box. CControl is derived from CComponent.

I cannot declare CLabel inside CEditBox unless I include CLabel in the header but I think I keep getting linker errors doing that even though I have all of my headers wrapped in #ifndef #define class #endif syntax but I'm also a noob. Instead, I have a CComponent* pointer declared since they are derived from that class.

Fine. Now in the constructor for the CEditBox I have:

#include "CLabel.h" //include in .CPP is fine I reckon.

CEditBox::CEditBox() {
    CLabel Field;      //Create CLabel
    InputType = ALL;   //Not important for my question related to allowed symbols
    Label = &Field;    //CComponent pointer to CLabel

}

When this constructor function returns, wouldn't CLabel go out of scope, and thus Feild would be destroyed and now my pointer is pointing at an undefined block of memory? What would be an appropriate way to do this? Is there a better solution?

Thank you

Linker problem

I don't know that the problem exists anymore, but some think this is a more important issue. Well here is the actual code now and you guys can tell me if you think it is done incorrectly. Base Class CMenuObject

#ifndef _CMENUOBJECT_H_
#define _CMENUOBJECT_H_
class CMenuObject {

protected:
    const char* ClassName;
public:
    CMenuObject();
    virtual const char* Object();

};

#endif

Next Class is CComponent

#ifndef _CCOMPONENT_H_
#define _CCOMPONENT_H_

#include "CMenuObject.h"

class CComponent : public CMenuObject {
protected:
    const char* _Name;
    int _Tag;
    static int _ComponentCount;
    static int _IDCount;

public:
    CComponent();
    virtual const char* Name();
    virtual int Tag();
    virtual void Tag(int t);


};


#endif

Then comes CControl These would be objects that users would interact with or in some way need control the display (i.e. a timer doesn't need user input) and is a mammoth. Don't mind the function pointer stuff because I have no idea what i'm doing with that yet.. this is my first guess approach to handling events. I think it's limiting because I can't figure out what to do if the function needs to take a parameter but I may not have to, etc... We can gloss over this detail for now.

#ifndef _CCONTROL_H_
#define _CCONTROL_H_

#include "CComponent.h"

class CControl : public CComponent {
protected:

    int _X,_Y,_Width,_Height;
    float R,G,B,A;

    void (*OnClk)();
    void (*OnDblClk)();
    void (*OnMOver)();
    void (*OnMHover)();
    void (*OnKDown)();
    void (*OnKUp)();
    void (*OnFcs)();

    bool Visible;

    CComponent* Pappy;


public:
    CControl();

    //Render Control
    virtual void Show();                                            //      Show Component
    virtual void Hide();                                            //      Hide Component
    virtual void OnRender();                                        //      Render Component

    virtual bool IsVisible();                                       //      Get Current Visibility Status

    //Paramater Control
        //Write
    virtual void X(int x);                                          //      Set Component's X coordinate
    virtual void Y(int y);                                          //      Set Component's Y coordinate
    virtual void Width(int w);                                      //      Set Component's Width
    virtual void Height(int h);                                     //      Set Component's Height
        //Read
    virtual int X();                                                //      Get Component's X coordinate
    virtual int Y();                                                //      Get Component's Y coordinate
    virtual int Width();                                            //      Get Component's Width
    virtual int Height();                                           //      Get Component's Height

    //Display Control
    virtual void Color(float r, float g, float b);                  //      Set Color of Component- Multicolored objects, this will be the base or bkg color.  Makes alpha 1.0f.
    virtual void Color(float r, float g, float b, float a);         //      Same as above but allows for input of an alpha value. 

    //Font Control
    virtual void FontName(const char* font);                        //      Name of font to use
    virtual void FontSize(int pt);                                  //      Pt size of font.  Or maybe pixel, no idea.
    virtual void Text(const char* msg);                             //      Text message to render
        //Read
    virtual const char* Text();                                     //      Read Text Message

    //Interactive Control                                           //      These will register call back functions for user events
    virtual void OnClick(void (*func)());                           //      On Single Click
    virtual void OnDoubleClick(void (*func)());                     //      On Double Click
    virtual void OnMouseOver(void (*func)());                       //      On Mouse Over
    virtual void OnMouseHover(void (*func)());                      //      On Mouse Hover
    virtual void OnKeyDown(void (*func)());                         //      On Key Down
    virtual void OnKeyUp(void (*func)());                           //      On Key Up
    virtual void OnFocus(void (*func)());                           //      On Focus 

    //Other
    virtual void Parent(CComponent);                                //      Set Parent
    virtual CComponent* Parent();                                   //      Get Parent
};

#endif

Finally my end game headers of CLabel and CEditBox.

#ifndef _CLABEL_H_
#define _CLABEL_H_

#include "CTexture.h"
#include "CFont.h"
#include "CControl.h"


class CLabel : public CControl {
private:

    const char* vText;

    CFont Font;

    CTexture Text_Font;
    SDL_Surface* Surf_Text;

    int X,Y,vWidth,vHeight;

public:
    CLabel();
    CLabel(const char* text);

    virtual void OnRender();
    virtual void OnCleanup();

    virtual void Text(const char* msg);
    virtual const char* Text();

    virtual void FontName(const char* fname);
    virtual void FontSize(int pt);
    virtual void FontColor(float r, float g, float b);
};


#endif

AND

#ifndef _CEDITBOX_H_
#define _CEDITBOX_H_

#include "CControl.h"

class CEditBox : public CControl  {
protected:

    CComponent* Label;
    int InputType;



public:
    CEditBox();
    ~CEditBox();
    virtual void OnRender();
    //virtual void OnCleanup();
    virtual void OnLoop();

    virtual void Text(const char* msg);
    virtual const char* Text();

    virtual void FontColor(float r, float g, float b);

    virtual void OnClick(void (*func)());                           //      On Single Click
    virtual void OnDoubleClick(void (*func)());                     //      On Double Click
    virtual void OnMouseOver(void (*func)());                       //      On Mouse Over
    virtual void OnMouseHover(void (*func)());                      //      On Mouse Hover
    virtual void OnKeyDown(void (*func)());                         //      On Key Down
    virtual void OnKeyUp(void (*func)());                           //      On Key Up
    virtual void OnFocus(void (*func)());                           //      On Focus 


    enum {
        ALL = 0,                //abcdefghijklmnopqrstuvwxyz (and caps) 1234567890!@#$%^&*()_+-=[]{}<>\/|"';:,.?
        ALPHA_NUMERIC,          //abcdefghijklmnopqrstuvwxyz (and caps) 1234567890
        ALPHA,                  //abcdefghijklmnopqrstuvwxyz (and caps)
        NUMERIC,                //1234567890
        PASSWORD,               //abcdefghijklmnopqrstuvwxyz (and caps) 1234567890!@#$%&.     -- Render as *
        IP                      //1234567890 .  Maybe fix feild width and force xxx.xxx.xxx.xxx format.
    }; 
};

#endif

[SOLVED]

Today, I found the one dang header not wrapped in #ifndef #define #endif. (it was CTexture which gets called again in CFont. Anyway, the restructure was also incredibly beneficial because I've figured out how to use inheritance and base class pointers, and how derived classes can work with each other. Not to mention many many more things. :)

The route I'm taking for derived class interplay is using a base class pointer that can access the derived classes functions via virtual functions. I use new and delete because that's what I am comfortable with. For everyone that contributed, thank you! They are all good answers.

Chemistpp
  • 1,721
  • 1
  • 21
  • 44
  • Makes since. Much like using structs and nodes in data containers. Okay, so would I need to take care of that to reduce memory leaks? Like `delete [] Label` – Chemistpp Jun 20 '13 at 15:25
  • Sorry, I've deleted my comment just before your answer. However, my comment is available as the answer below. – Andrejs Cainikovs Jun 20 '13 at 15:27
  • Yeah, your answer was there right after I dropped my comment with the answer to my comment. +1 to ESP. – Chemistpp Jun 20 '13 at 15:28
  • Even though `new`is correct in this case I would advice you to fix the bug with the include guards. Avoid `new` as long as possible to avoid bugs. – Xale Jun 20 '13 at 15:29
  • I may have. See what happened was I am not a programmer by trade (obviously) but I have had formal school in it, I just chose a different major and was hinging on programming for the first 2 years of college. Because of this, I've never implemented a such a structure before. We focused more on data structures. When I started, I started from the branch (making a label and an edit box) and trying to link them together and I was getting all sorts of issues when trying to compile but if I removed the include and commented out the pointers for those structures, it compiled. – Chemistpp Jun 20 '13 at 15:36
  • Since I've completely restructured from the root and am almost near compiling it. The includes in my headers are all much cleaner now. I can try and do it, but I have used new several times back in school with no problems. I never lost a memory leak point ;) – Chemistpp Jun 20 '13 at 15:38

4 Answers4

3

Stereo typical approaches would be:

Showing the second approach:

//////////// CEditBox.hpp header file
#include <memory>
#include <string>

class CLabel; // forward declaration

class CEditBox
{
  public:
    CEditBox(std::string const&);
  private:
    std::unique_ptr<CLabel> _label;
};

The forward declaration avoids the need to include CLabel.hpp. The unique_ptr manages the lifetime _label so we don't have to remember to delete it.

//////////// CLabel.hpp header file

#include <string>
#include "CLabel.hpp"

class CLabel
{
  public:
    CLabel(std::string const& name) 
        : _name(name) 
    {
    }
  private:
    std::string _name;
};

Just a sample, nothing to see here. Let's move on:

///////////// CEditBox.cpp source file

#include "CEditBox.hpp"
#include "CLabel.hpp"

CEditBox::CEditBox(std::string const& name)
    : _label(new CLabel(name)) 
{
}

That's the magic: we integrate it all by include CLabel.hpp as well, and construct it in the initializer list.

///////////// main.cpp source file

#include "CEditBox.hpp"

int main()
{
    CEditBox box("Hello world"); // no need to 'know' CLabel here   
}

Proof of the pudding is in the compilation: http://ideone.com/zFrJa8

Community
  • 1
  • 1
sehe
  • 328,274
  • 43
  • 416
  • 565
  • Yeah, I've got no idea what the heck this pimpl is and mang that syntax is confusing. Let me ask: _label is a pointer to an edit box. Okay, this is where I'm beyond loss. CLabel(std::string name) : _name(std::move(name)) {} do? CLabel declares a variable of type CLabel but what does (std::string name) do to it? Is that calling some sort of constructor? I'm lost. I *think* what is happening is that you are moving the memory for a label into CEditBox but I really don't understand the syntax. Mainly the one line I pointed out. – Chemistpp Jun 20 '13 at 15:48
  • @Chemistpp Just caught your comment. Firstly, it's hardly my fault if C++ syntax is a bit much to take in. And after all, it appears you actually want to _learn_ it? Anyways, I formatted the code a bit more friendly and removed the not-so-relevant use of `std::move`. Note that I already **named** the syntax if _constructor initializer list_ (`CLabel() : _name(name)`) so [google should help you along the way](https://www.google.com/search?q=constructor+initializer+list) if that was new for you. – sehe Jun 20 '13 at 21:02
  • I hope it'll be clearer now. For the record, I didn't show pimpl as it is effectively the 'advanced' version of basic techniques you see here. – sehe Jun 20 '13 at 21:03
  • I understand now. : just lets us define the value _label as being a new CLabel(name)) and from my understanding, std::unique_ptr LookAtThat essentially will delete the memory that LookAtThat is pointing to when it goes out of scope. So really, there isn't much difference between the first answer and this other than the crafty definition in the header file and the automatic freeing of memory. – Chemistpp Jun 20 '13 at 22:01
  • 1
    Also, yes, I never do anything just for an answer or to just memorize, always for understanding. I appreciate the explanation. – Chemistpp Jun 20 '13 at 22:03
  • Okay, so pimpl does a forward declaration of a struct, `struct TStruct;` in the class header file. Then creates one private member pointer, possibly you could use a smart pointer like `std::unique_ptr PrivateVars` inside said class. Then in `class.cpp`, I can define the struct, fill it with members because the only access is a private member that points to it. – Chemistpp Jun 20 '13 at 23:28
  • I think that that method is more annoying to implement than forward declaring the class itself and create private members in the class declaration. However, the website I read mentioned there is some benefit but I don't quite understand it's point. Something to do with changing private members and having to adjust all the files and not just one. http://c2.com/cgi/wiki?PimplIdiom Thanks for your response too. I really think everyone deserves reputation but I think I can only flag 'one answer' so you all got voted up anyway :) – Chemistpp Jun 20 '13 at 23:30
  • I think you are beginning to see it. `unique_ptr` has "unique ownership semantics" which is indeed close to a regular _aggregated_ member of type CLabel. Not the same though: if CLabel isn't copyable/movable then the wrapper would allow for explicit transferring of the ownership (try that with a plain member variable!). Another difference is that unique_ptr employs dynamic allocation and it will have (potentially significant) runtime cost. – sehe Jun 21 '13 at 06:24
  • Also, the point of Pimpl is to hide **all** implementation details from (public) headers. This could be for **(a)** encapulation: users of the header cannot (inadvertently) depend on implementation details that might change, users cannot _abuse_ properties that are part of a class's internals. _This is important for API designers/library writers._ **(b)** reducing compilation times; some implementations drag in a lot of headers and template instantiations and one would not want to have them appear in a central header, because compilation times could be severely affected. – sehe Jun 21 '13 at 06:28
1

Your thinking is on the correct track. The right approach would be allocating this object dynamically, i.e.

Label = new CLabel;

Don't forget to free memory in destructor:

delete Label;
Andrejs Cainikovs
  • 23,842
  • 2
  • 65
  • 84
  • Please, don't teach new/delete in C++. Especially to "beginners". It's unnecessarily error prone. – sehe Jun 20 '13 at 15:41
  • Memory management is something that should be learned as a first basis if you are talking about such languages like C, and C++. – Andrejs Cainikovs Jun 20 '13 at 15:46
  • 1
    I have 2 years of experience with C, C++ and assembly. I understand memory fantastically. If you look, I asked him in his comment before he even mentioned it that I should be deleting the memory. So far, this is the simplest method, easiest to understand, and the exact answer to my question. – Chemistpp Jun 20 '13 at 15:52
  • My weakest area is syntax knowledge and large program development (structure). I have had very little exposure to reading code, especially in the last few years. I only had 2 semesters of C++, and it was all console based algorithms and data structures (not programming but more like creating nodes and linking them with pointers and algorithms to sort, search, and navigate) so inheritance is completely new to me. – Chemistpp Jun 20 '13 at 15:55
  • @AndrejsCainikovs Well, I completely agree. Only: memory management != `new`/`delete`! So yes, teach memory management (value/reference semantics, too) early. But teach `new` only near the end (at least after containers and algorithms). – sehe Jun 21 '13 at 06:33
1

If I understand your problem correctly, you have two classes which have member variables with each other's class?

For instance:

// A.h
#ifndef A_H
#define A_H

#include "B.h"

class A {
public:
    ...
private:
    B* pB;
};
#endif // A_H

And:

// B.h
#ifndef B_H
#define B_H

#include "A.h"

class B {
public:
    ...
private:
    A* pA;
};

#endif // B_H

And compiling these together causes some form of linker error? If this is the case you can circumvent this by forward declaring the class, so instead of including A.h and B.h you simple write class B; above your declaration of class A and class A; above your declaration of class B, then include the headers in your cpp files. So A.h would look like:

// A.h
#ifndef A_H
#define A_H

class B;
class A {
public:
    ...
private:
    B* pB;
};
#endif // A_H
Thomas Russell
  • 5,480
  • 3
  • 28
  • 61
  • Yeah! I heard you can forward declare classes, as long as the implementation is run for the forward declared classes before you try to access their members. For some reason, I thought this seemed a bit oddball and I knew there had to be a way to do it since I've seen examples using base class pointers. – Chemistpp Jun 20 '13 at 15:32
0

CLabel Field; should be a member of CEditBox. The real question is the one where you mention 'linker errors' (or whatever it is). That's the problem you should be solving.

user1764961
  • 693
  • 6
  • 21
  • Yeah, I think it came from the poorly structured system. I'm just trying to get this last thing done to check and make sure it's linked properly now. I also heard I could create a "Implement.h" file and essentially implement everything in that file in correct order so that way all of the other files can just include that one header file. Not sure if that is the smart choice. – Chemistpp Jun 20 '13 at 15:51
  • Well, provide some info on this problem including the real error and some part of source and people here will try to help you. – user1764961 Jun 20 '13 at 15:55
  • I will add it but I've bastardized my files so much I couldn't get the error message back again. And it's been completely restructures do I don't even know the problem exists anymore. – Chemistpp Jun 20 '13 at 15:56