Jump to content

C++ OOP confusion with constructor/destructor behaviour...

pipnina

I'm using some OOP in my college work, but I have two problems:

1: I can't figure out how to remove the objects I have made (They stay in memory even after clearing the vector they are stored in) 

2: When I add more, there is a chance that the destructor will get called immediately afterwards and remove it. Then at some point it will go through a long streak of calling the constructor but not the destructor (causing the program's memory usage to go up)

 

The relevant code:

Class declaration (BOX.h)

class BOX{
    public:
        static const int maxsize_X = 25,
                         maxsize_Y = 25;
        static constexpr float maxVelocity = 10.0f;

        BOX();
        ~BOX();
    private:
        int sixeX,
            sizeY;
        int posX,
            posY;
        std::vector<float> boxData;

        void generateBox();
        void rotateBox(float);
        void renderBox();
};

Class implementation (BOX.cpp)

BOX::BOX(){
    cout << "l";
}

BOX::~BOX(){
    cout << "u";
}

void BOX::generateBox(){
    boxData.erase(boxData.begin(), boxData.end());
    for(int i=1; i<=maxsize_X*maxsize_Y; i++){

    }
}

void BOX::rotateBox(float x){

}

void BOX::renderBox(){

}

Code involved that creates the classes (also in BOX.cpp, this probably isn't very good practice...)

vector<BOX> boxes;

void newBox();

void boxDecisions(){
    if(KEY_DOWN(VK_ESCAPE)){
            //boxes.erase(boxes.begin(), boxes.end());
            location = 1;
            Beep(500, 100);
    }
    if(KEY_DOWN(VK_RETURN)){
        newBox();
    }
    dimensionCheck(boxScreenSize);
}

void newBox(){
    BOX new_box;
    boxes.push_back(new_box);
}

void boxDecisions(); gets called as quickly as the CPU can go (one thread) as long as the 'location' variable is equal to 4 (location 1 is the menu)

 

KEY_DOWN(VK_ESCAPE) would be a #define macro (#define KEY_DOWN(vk_code) GetAsyncKeyState(vk_code) ? 1:0)

 

The output when holding down ENTER key would be:

 

uqX7IPZ.png

(u = constructor, L = destructor)

 

I'm not at all sure why the constructor isn't being called when I hit escape (even when the line isn't commented out haha) or why the destructor is being called immediately after the constructor, when I haven't told it to run. (Not knowingly, anyway)

 

If anyone who knows why it's behaving like this, please help! 

Link to comment
Share on other sites

Link to post
Share on other sites

2 hours ago, pipnina said:

(u = constructor, L = destructor)

You have that backwards.

Quote

I'm not at all sure why the constructor isn't being called when I hit escape

Because you're only calling erase, not making any new objects.

 

Quote

why the destructor is being called immediately after the constructor

Inside newBox you're creating a temporary object called new_box (this calls the constructor), copying that object into the vector (calls the copy constructor) then destroying the temporary object (calls the destructor.) To avoid that unnecessary copy and destructor look up vector.emplace_back.

 

edit: those long chains of destructors are when you fill the vector so it has to allocate more space, copy everything into the new space then destruct all the old copies.

1474412270.2748842

Link to comment
Share on other sites

Link to post
Share on other sites

2 hours ago, fizzlesticks said:

You have that backwards.

Because you're only calling erase, not making any new objects.

 

Inside newBox you're creating a temporary object called new_box (this calls the constructor), copying that object into the vector (calls the copy constructor) then destroying the temporary object (calls the destructor.) To avoid that unnecessary copy, look up vector.emplace_back.

I replaced vector.push_back() with emplace_back() (with no arguments). It still provides that pattern seen in the image in my OP. Weirdly enough, since the Us are the destructors, it's weird that whenever a large block of them shows up, the ram usage in the program jumps upwards. But when it's still a solid line of Ls, it goes up slowly.

Surely the destructors being called would be freeing memory and not using more? I read that in destructors you sometimes need to remove member variables manually, is this the case here, or can I leave the destructor blank because I'm not using 'new'?

 

Also, after uncommenting the boxes.erase line, I found that the destructors are being called as the program moves back to the menu, but the memory used is not freed. 

Link to comment
Share on other sites

Link to post
Share on other sites

14 minutes ago, pipnina said:

Weirdly enough, since the Us are the destructors, it's weird that whenever a large block of them shows up, the ram usage in the program jumps upwards. But when it's still a solid line of Ls, it goes up slowly.

When the long block of destructors happen you're deallocating a bunch of space but your also allocating even more space to make room for more the new boxes.

 

If your vector has space for 100 items and you try to add an 101st, the program will allocate a big chunk of memory (typically 1.5x or 2x the size of the old vector.) So the memory usage will grow by the size of the new vector then shrink by the size of the old vector and since the new one is larger you end up with more used memory.

1474412270.2748842

Link to comment
Share on other sites

Link to post
Share on other sites

On 11/24/2016 at 5:02 PM, fizzlesticks said:

Inside newBox you're creating a temporary object called new_box (this calls the constructor), copying that object into the vector (calls the copy constructor) then destroying the temporary object (calls the destructor.) To avoid that unnecessary copy and destructor look up vector.emplace_back.

Potentially important note: 

vector::emplace_back()
list::emplace_back()

Were introduced with C++11, which may not be kosher to use in your college work. We only really covered C++11 towards the end, and it was typically not allowed solely because our programs were built and tested by our teachers on our somewhat old UNIX systems. Full C++11 support starts in g++ 4.8.1. 

 

Additionally, your optimizing compiler will probably generate the exact same code from the two uses (push_back() vs emplace_back()). However, that cannot be guaranteed on any system.

 

Just some things to think about.

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×