0

In my class implementation, I have something like this:

base class

class swcWidget :
     public swcRectangle
{
public:
    swcWidget();
    virtual ~swcWidget();

    void update(float dt);

protected:

    inline virtual void oPaintOnTop() { }
private:
};

derived class

class swcButton :
     public swcWidget
    ,public swcText
{
public:
    swcButton();
    virtual ~swcButton();

    static const int DEFAULT_SIZE = 20;

protected:
private:

    void oPaintOnTop();
};

class swcApplication
{
public:

    swcApplication(int argc, char *argv[]);
    virtual ~swcApplication();

    int run();

    struct Controls
    {
        typedef std::vector<swcWidget*> vWidgets;                 //option 1

        ~Controls();


          /////////////////////////////////
         //   M A I N   P R O B L E M   //
        /////////////////////////////////

        void add(swcWidget &&widgets);  //most preferred option
                                        //but gets demoted to base class.

        void add(swcWidget *widgets);   //second choice
                                        //but should I make a copy of it?
                                        //or just make a reference to it?
                                        //and this one does what I wanted to.
                                        //but still unsure on other things I don't know

        void add(swcWidget *&&widgets); //this compiles fine (?)
                                        //I don't know what kind of disaster I can make into this, but still does not do what I wanted.

        inline vWidgets &getWidgets() {
            return widgets;
        }

    private:

        vWidgets widgets;
    };

    Controls controls;

};

I know some working option like this:

making the

swcApplication::Controls::widgets

as type of

std::vector<std::shared_ptr<swcWidget>>

but my code will bind into std::shared_ptr and I cannot make simple syntax like this:

swcButton btn;
app.controls.add(std::move(btn));

Example usage:

main.cpp

int main(int argc, char *argv[])
{

    swcApplication app(argc, argv);

    app.windows.create(640, 480);

    if (font->load("fonts\\georgia.fnt") != BMfont_Status::BMF_NO_ERROR)
    {
        puts("failed to load \"georgia.fnt\"");
    }

    {
        swcButton btn;

        btn.setPosition(100, 100);
        btn.setFont(font);
        btn.text = "Ey!";

        app.controls.add(std::move(&btn));

//      btn.text = "Oy!";

    }


    return app.run();
}

Update:

Here's the temporary definition of swcApplication::Controls::add() although it may still vary

void swcApplication::Controls::add(swcWidget &&widget)
{
    widgets.push_back(std::move(widget));
}
mr5
  • 3,438
  • 3
  • 40
  • 57

1 Answers1

1

If a class is moveable, then it will in turn move it's members one by one. For this to be efficient, these members must either be small POD's or must be allocated on the heap. You must add this functionality, not forget to move any member, and object slicing is a concern to watch out for.

Given the class is non-trivial, you have the most efficient move construct available when you just use a pointer directly (at the cost of heap allocation time of course). No slicing is possible, and no member can be forgotten to be moved, since you move the whole object in one go. The one hurdle to watch out for is to keep track of who owns the pointers - you'd better set it in stone, but if that's done then there are no issues anymore.

The move semantics are wonderful, but if your classes are somewhat involved I think pointers in this case are easier / more efficient to work with. I'd thus stick with the pointer variant, and make sure your collection will own the pointers (and release them again via RAII) - make liberal use of comment in your public interface saying so. You can do this by storing some form of smart pointer (hint: be careful with unique_ptr's!), or (less safe) make and always use a Clear() member that delete's all pointers before clear()'ing the collection.

EDIT

Whet you define your widgets member to be of type vector, then example code could be:

To class swcApplication add:

  void swcApplication::Controls::ClearWidgets() {
    for (auto& nextWidget: widgets) {
      delete nextWidget;
    }
    widgets.clear();
  }

Don't forget to call ClearWidgets at the appropriate times (like in your destructor).

Adding widgets can be done with:

// Note: any passed widget will not be owned by you anymore!
template <typename Widget>
void swcApplication::Controls::add(Widget*& widget) {
  widgets.push_back(widget);
  widget = nullptr;
}

From now on you can add widgets like

swcButton* btn = new swcButton;
app.controls.add(btn);
// btn is now owned by app.controls, and should be set
// to nullptr for you to prevent misuse like deleting it

Using a smart pointer here should make it more safe, though storing unique_ptr's makes accessing them a bit error-prone (watch out for grabbing ownership back from the container when accessing them), and a shared_ptr gives overhead which might be unneeded here.

Carl Colijn
  • 1,423
  • 9
  • 29
  • Thanks for suggesting that I should create those on heap. But I'm still confused if I should use `std::move` if I choose the method `add(swcWidget *widget)` on it? like this syntax `add(std::move(widget_created_on_heap))` then inside the `add` method.. `widgets.push_back(std::move(widgets_on_heap));` ?? – mr5 Feb 11 '14 at 12:24
  • How do you declare "widget_created_on_heap"? If it's declared as a swcWidget *, then there's not much to move :) - i.e.: then you'd move a raw pointer, not an object. In that case I'd just pass in the pointer to your widget itself directly. Note that the container will own it from then on, so do not do a delete on it afterwards yourself. To prevent anyone from doing just that, you could also make add()'s widget argument a *&, and in the last line of add() set it to nullptr to prevent this. – Carl Colijn Feb 11 '14 at 12:34
  • Yup! Uhm, another question again, is this the proper way to delete those objects: `for (auto &i : widgets) { delete i; } widgets.clear();` ?? (widgets is declared as `std::vector` – mr5 Feb 11 '14 at 13:24
  • If I do this `add(swcWidget *&)` I get compiler errors. What's the right syntax to do this `swcWidget *widget = new swcWidget; app.controls.add(widget);` ? – mr5 Feb 11 '14 at 13:47
  • @mr5: Yes, the deletion code looks good. About add: your declaration seems ok... I've updated the answer to show the correct code. – Carl Colijn Feb 11 '14 at 16:08
  • But the code you present here in `app.controls.add(btn)` won't compile in mine. it says: " no known conversion from 'swcWidget*' to 'swcWidget*&' " – mr5 Feb 11 '14 at 16:13
  • @mr5: Sorry, my bad; you cannot pass a derived class via ref-to-pointer-to-base (if it would be possible, you could open a huge can of worms there). What you can do is drop the ref and loose the auto-set-to-nullptr, or make add a template function. I've updated my answer with the last version. – Carl Colijn Feb 11 '14 at 16:23
  • Ahm, follow up question. What if I do this `swcWidget *widget = new swcWidget[10];` how am I gonna **add** and **delete** those? – mr5 Feb 12 '14 at 10:44
  • 1
    You don't; each `new[]` must be matched by a `delete[]`, and so you cannot pass a batch of widgets allocated in one block with `new[]` to someone that takes ownership but expects them to be individually allocated and so later `delete`'s them (without the []). I don't know why you want to create 10 widgets in one go (you still have to set them up individually anyway), but if the repeated calls to `new` bother you you could create a factory method to allocate and initialize (and maybe even add) a new widget in one go. – Carl Colijn Feb 12 '14 at 14:21