Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Class design - easily init / build objects

Using C++ I built a Class that has many setter functions, as well as various functions that may be called in a row during runtime. So I end up with code that looks like:

A* a = new A();
a->setA();
a->setB();
a->setC();
...
a->doA();
a->doB();

Not, that this is bad, but I don't like typing "a->" over and over again.
So I rewrote my class definitions to look like:

class A{
public:
    A();
    virtual ~A();

    A* setA();
    A* setB();
    A* setC();
    A* doA();
    A* doB();

    // other functions

private:

    // vars
};

So then I could init my class like: (method 1)

A* a = new A();
a->setA()->setB()->setC();
...
a->doA()->doB();

(which I prefer as it is easier to write)
To give a more precise implementation of this you can see my SDL Sprite C++ Class I wrote at http://ken-soft.com/?p=234

Everything seems to work just fine. However, I would be interested in any feedback to this approach. I have noticed One problem. If i init My class like: (method 2)

A a = A();
a.setA()->setB()->setC();
...
a.doA()->doB();

Then I have various memory issues and sometimes things don't work as they should (You can see this by changing how i init all Sprite objects in main.cpp of my Sprite Demo).
Is that normal? Or should the behavior be the same?
Edit the setters are primarily to make my life easier in initialization. My main question is way method 1 and method 2 behave different for me?

Edit: Here's an example getter and setter:

Sprite* Sprite::setSpeed(int i) {
    speed = i;
    return this;
}

int Sprite::getSpeed() {
    return speed;
}
like image 831
Kenny Cason Avatar asked Aug 04 '10 13:08

Kenny Cason


2 Answers

One note unrelated to your question, the statement A a = A(); probably isn't doing what you expect. In C++, objects aren't reference types that default to null, so this statement is almost never correct. You probably want just A a;

A a creates a new instance of A, but the = A() part invokes A's copy constructor with a temporary default constructed A. If you had done just A a; it would have just created a new instance of A using the default constructor.

If you don't explicitly implement your own copy constructor for a class, the compiler will create one for you. The compiler created copy constructor will just make a carbon copy of the other object's data; this means that if you have any pointers, it won't copy the data pointed to.

So, essentially, that line is creating a new instance of A, then constructing another temporary instance of A with the default constructor, then copying the temporary A to the new A, then destructing the temporary A. If the temporary A is acquiring resources in it's constructor and de-allocating them in it's destructor, you could run into issues where your object is trying to use data that has already been deallocated, which is undefined behavior.

Take this code for example:

struct A {
    A() { 
        myData = new int;
        std::cout << "Allocated int at " << myData << std::endl;
    }
    ~A() { 
        delete myData; 
        std::cout << "Deallocated int at " << myData << std::endl;
    }
    int* myData;
};

A a = A();
cout << "a.myData points to " << a.myData << std::endl;

The output will look something like:

Allocated int at 0x9FB7128
Deallocated int at 0x9FB7128
a.myData points to 0x9FB7128

As you can see, a.myData is pointing to an address that has already been deallocated. If you attempt to use the data it points to, you could be accessing completely invalid data, or even the data of some other object that took it's place in memory. And then once your a goes out of scope, it will attempt to delete the data a second time, which will cause more problems.

like image 75
Collin Dauphinee Avatar answered Sep 18 '22 12:09

Collin Dauphinee


What you have implemented there is called fluent interface. I have mostly encountered them in scripting languages, but there is no reason you can't use in C++.

like image 40
soulmerge Avatar answered Sep 20 '22 12:09

soulmerge