• Advertisement
Sign in to follow this  

Object Usage and Destructors

Recommended Posts

Hello everyone, I have been banging my head trying to understand the following.  First, let me note that the correct way of doing this would be with smart pointers, but my curiosity has really gotten the better of me!  

 

Here is my code : https://paste.ofcode.org/guvXn7iz5tWtZ6mRHEdR2S

 

I am trying to understand why these objects are not calling the destructors:  (maybe the keyword new and delete are always needed in these cases?)  I couldn't find any examples without these keywords or smart pointers.  I know, I know, use smart pointers instead.

 

 

 

A testObject1

AtestObject2

 

 

B* testObA;

B testObb;

A test_1;

A * test_2;

 

 

B * testOb1;

B  testOb2;

//THIS ONE DID CALL "destructor,"  why?

A  testObjA;

A * testObjB;

 

Thank you so much,

Josheir

Share this post


Link to post
Share on other sites
Advertisement

I assume you are asking why there is no destructor call to A::~A() for "testObjA?"

That's because testObjA isn't an A, it's a pointer-to-an-A. There are no destructors to execute for pointers, just like there are no destructors to execute for other basic language types like integers and floats. 

Letting a bare pointer-to-A (or any other bare pointer type) go out of scope does not invoke the destructor for A on that instance (as that instance is not an A).

Share this post


Link to post
Share on other sites

It's not clear exactly what you're asking since there are a bunch of different variables there, and that's too complex a test case to examine in detail. 4 variables created in main, 4 more in func, then you call func from main - that's potentially 8 things you could be asking about. But I'll tell you what I see.

When you create a local instance of a variable inside a function - e.g. B testOb2() - , it gets destroyed when it goes out of scope (i.e. when the function returns), so its own destuctor will be called.

When you create a member variable - e.g. A testObject1 inside Class B - that variable gets destroyed when the enclosing object is destroyed.

When you create a pointer to something - e.g. B * testObA - you haven't actually created an object, just a pointer that could hypothetically refer to one. The same applies to creating a member pointer. Just like any variable, when it goes out of scope or is destroyed, its destructor is called, but a pointer's destructor does nothing.

This is where smart pointers come in - the reason we call them smart, is because they do have a destructor, and what that destructor does is destroy the object that they own. This prevents that object 'leaking', i.e. being allocated but completely unusable, thereby wasting memory.

Note that none of your pointers actually own or refer to anything at all, so there is nothing to leak. However, they're also completely useless as-is.

Share this post


Link to post
Share on other sites

C and C++ only delete data that is in a scope which is about to be left. Bare pointers in a scope that is being left are deleted too, that is, only the pointer itself, not the data it points to. C and C++ don't track if data was malloc-ed (or new-ed), or if you created a pointer to some variable, like

{
  int i;
  {
    int *p = &i;
    *p = 3;
  } // Leaving scope, delete p (only the pointer, not i).
    // "delete &i;" is not allowed, as "i" was never "new-ed".

  printf("i=%d\n", i); // This would fail too if i would be destroyed.
} // destroy i

Similarly, you may setup several pointers to the same block of memory. C/C++ doesn't track that information, it's your responsibility to make sure you delete the memory just before destroying the last pointer to it.

 

Reference-basd languages, like Python, C#, and Java do track references to a block of data (in several different ways), and can detect all the above cases, and release memory for you.

For C/C++ you can use Boehm's garbage collector.

Share this post


Link to post
Share on other sites

Well, first of your code here is different to whats in your link:

B * testObA;
B testObB();
A test_1; // destructor called
A * test_2;

So you are now asking why only line 3 calls the destructor, if I am right? Thats quite simple:

- "B* testObA" declares a pointer to an object of type B, not an object itself. As a pointer just stores the address of another object, obviously it does not call the destructor, with doing:

B * testObA = new B; // creates & assigns an object of Type B
delete testObA; // deletes the object pointed to by testObA => calls the destructor

(you're actually right that you should rather use unique_ptr in that case :) )

- The second line "B testObB();", at least how it is the code you linked, does not declare a variable of type B eigther, it declares a function pointer with the signature "B (*)(void)". Yeah, having empty brakes like that when declaring a variable does not call the constructor but instead modifies the type of the variable. So when you remove the (), it should construct & destruct the object properly.

 

Point 2 can actually be misleading, but pointers are pretty basic C++-stuff, so I think this thread rather belongs to for beginners; and on that note you should read up on basic concepts of c++ memory "management" like pointer, references etc... from how it appears :)




			
		

Share this post


Link to post
Share on other sites

OK, than so if I needed to destroy a dynamically allocated object in p_Object and p_Object is in another object,  How would I destroy p_Object's contained dynamic objects when p_Objects containing class goes out of scope.

Thanks again,

Josheir

Share this post


Link to post
Share on other sites

That's what a destructor is for. Object A's destructor is there to allow you to clean up anything that Object A allocated during its lifetime. So, if you used `new` to allocate some sub-objects (e.g. in the constructor, or anywhere else), you use `delete` in the destructor to destroy those sub-objects.

Share this post


Link to post
Share on other sites

I've really made a mess of this.

My code was not printing out the destructor calls and it now is.

 

Basically though if a pointer to an object doesn't call the destructor than how can it's subobjects be deleted?

 

thanks,

Josheir

Share this post


Link to post
Share on other sites

The object that contains the pointer is responsible for that pointer, and for whatever the pointer refers to. So it must destroy that sub-object, within its own destructor.

eg.

class A
{
private:
    B* objB;

public:
    A()
    {
        objB = new B();
    }

    ~A()
    {
        delete objB;
    }
}

Share this post


Link to post
Share on other sites
class A
{
private:
    B* objB;

public:
    A()
    {
        objB = new B();
    }

    ~A()
    {
        delete objB;
    }
}

So, what if objB = new B() was never used?

Would B not be an object and therefore no need to delete it (it would be a pointer to no object)?

 

And before smart pointers: if there are objects of B in A and they need to be destructed do we need to use the new/delete so that B's destructor can handle them?

 

Almost got it I think,

Josheir

Edited by Josheir

Share this post


Link to post
Share on other sites

Because when you create a class instance like this,

SomeClass SomeClassInstance;

it gets created on stack memory which is automatically managed. You don't have to delete the object instance here, it is automatically done.


And when you create a class instance like this with new word (Which you did not use)

SomeClass* SomeClassInstance = new SomeClass();

It gets created on heap memory and you're required to manually delete it when you're done with it. The destructor is then called when you delete it. 

 

EDIT: Whoops, did not knew it already has better answers. 

Edited by newtechnology

Share this post


Link to post
Share on other sites

If you didn't create an object and make objB point to it, then no, there's nothing to clear up regarding that objB pointer. It's just a 'dangling' pointer that isn't doing anything.

(It doesn't make sense to say "Would B not be an object" - B, in this example, is a class. The objB variable is a pointer, and it points to an instance of class B.)

The objects inside B are for B to clear up. Whether you made the instance of B via 'new' or not is irrelevant.

Share this post


Link to post
Share on other sites

 

So, what if objB = new B() was never used?

Undefined behavior. If you had initialized objB to nullptr, then it would be pretty safe to call delete. Using delete on nullptr initialized pointers is fine. Using delete on uninitialized pointers would have undefined behavior.

Edited by newtechnology

Share this post


Link to post
Share on other sites

All right last try...

if new and delete are used the advantage is that the destructor is called so the object can handle the inside objects.

if the there is an internal object and it is not a pointer, the destructor will be called when it goes out of scope.

And finally if a pointer to an object is used and there is objects in it that need to be destroyed than you should have used new/delete so that the destructor is called to handle these objects (once again not using smart pointers!)

 

Phew I'm hoping this is it, thanks,

Josheir

Share this post


Link to post
Share on other sites

That looks roughly right. 

Just remember a pointer to something is not the instance itself.  A pointer points to an object instance.   Object instances are created and destroyed independent of the pointer that may point to them.

You can have many pointers that all point to the same object instance. You may have hundreds of pointers that may point to the object, but you should only have one thing that actually controls the lifetime of the instance; only one thing should allocate or destroy the object instance itself.  These days usually that one thing is a container or smart pointer.

Regarding new and delete, in modern code it is very rare to use either of those. These days if you see new and delete in the source code you should probably ask if it would be better to use a more modern option. Container classes and smart pointers are generally the better way to create and destroy objects and control their lifetimes, although you will still use pointers to point to those objects. They are a little misnamed, but smart pointers control the lifetime of the object instance, and they ensure the object instances are properly destroyed even if there are strange code paths like exceptions. 

 

/EDIT:  Also a gentle reminder that this is For Beginners, not the more technical forums.

Edited by frob

Share this post


Link to post
Share on other sites

"if new and delete are used the advantage is that the destructor is called"

No.

The destructor is called for any object when that object is destroyed. The object is destroyed when it goes out of scope, or the object containing it is destroyed.

 

"if there is an internal object and it is not a pointer, the destructor will be called when it goes out of scope."

If by 'internal object' you mean a member of some enclosing object, then it is destroyed when the enclosing object is destroyed.

If by 'internal object' you mean a local variable inside a function or whatever, then it is destroyed when it goes out of scope.

 

"And finally if a pointer to an object is used and there is objects in it that need to be destroyed than you should have used new/delete so that the destructor is called to handle these objects (once again not using smart pointers!)"

Not exactly. It doesn't make sense to say "you should have used new", because (as above) using new makes absolutely no difference to whether a destructor is called. The destructor is called when the object is destroyed, no matter how it gets destroyed, or how it got created.

If you created an object with 'new', and don't later 'delete' it when the pointer that refers to it has gone, that's a memory leak. The object still exists somewhere in memory but you can never use or reach it, so that's not idea.

 

The purpose of 'new' is to create an object that will outlive the current scope. The most common situation where this happens is that you call 'new' inside a constructor to create a sub-object, and assign it to a pointer inside the object. This is what my last code example did with objB. There are likely to be other functions that operate on the object during the running of the program, then at the end, when the enclosing object gets destroyed, the destructor for that object ensures that all sub-objects are cleaned up, by explicitly calling 'delete' on anything that was created via new.

You might be wondering why we'd have a pointer inside of a class and create an object via new, instead of just having the object inside the class directly.  Usually, if you can, you do want to just contain the object directly and avoid all the pointer stuff. But there are various reasons why that may not be the ideal approach; reasons that you probably don't need to worry about yet.

Share this post


Link to post
Share on other sites

Sounds great, I just wanted to understand the past ways too, I think it's important.

 

When 'new' was being used and an instance of an object was pointed too using it, did people ever instead put the object in with the data members and point to it for example in the constructor?

 

Besides what we've mentioned, what were the other pros and cons of using new/delete versus using this more regular non new/delete method of pointing to an instance.

 

Thank you,

Josheir

Share this post


Link to post
Share on other sites

That question doesn't make sense to me. Could you rephrase, being very specific with your terms?

Share this post


Link to post
Share on other sites

In an object that has a different object,

 

new can be used to create a pointer to that object as shown.

 

What about creating a pointer to that object that is a data member instead?

 

Is this good or bad for what reasons?

 

Thank you,

Josheir

Share this post


Link to post
Share on other sites
Not exactly. It doesn't make sense to say "you should have used new", because (as above) using new makes absolutely no difference to whether a destructor is called. The destructor is called when the object is destroyed, no matter how it gets destroyed, or how it got created.

 

Well what I was saying is if there is  some different type object in an object and it is pointed to it better not be a regular pointer or we will never be able to destroy any of it's different typed objects that may need deleting.

delete causes a deconstructor.

(tired,)

josheir 

Edited by Josheir

Share this post


Link to post
Share on other sites

"In an object that has a different object, new can be used to create a pointer to that object as shown."

If object A already 'has' object B, then you wouldn't be calling new. New creates an object, and returns a pointer to it.

Object A might contain a data member that is a pointer, something like B* objB, which on its own doesn't point to anything. You need to assign a valid pointer to that for it to be usable. Usually that means creating an instance of Class B via 'new', and assigning that value to the variable called 'objB'. Later, you'd want to call delete on that pointer, so that the object that was created with new gets destroyed properly.

There are ways to create pointers other than via new, ways which don't actually create a new object, but that's out of the scope of this discussion.


Well what I was saying is if there is an some different type object in an object and it is pointed to it better not be a regular pointer or we will never be able to destroy any of it's different typed objects that may need deleting.

 

The fact that the types involved may be different is irrelevant.

Regular pointers are okay. You just have to remember to call delete on them at the appropriate time. Destructors exist for this reason, giving you an opportunity to do any clean-up code when an object is going out of scope - such as deleting any sub-objects that it owns and has pointers to.

Smart pointers are just wrappers for normal pointers, but they have their own destructor that calls delete on the object they point to, meaning you don't have to remember to do it.

Share this post


Link to post
Share on other sites

I think I understand what you're asking.

In an object that has a different object,
 

From your example code, class B contains a pointer to an object of type A:  A * testObject2;

new can be used to create a pointer to that object as shown.   What about creating a pointer to that object that is a data member instead?
 

In that code the data member testObject2 is a pointer, but initially it doesn't point to anything in particular. You should not use it yet because you have no idea what it points to, and consequently it will probably crash your program or worse.  You need to actually point the pointer to something.  Usually you point things to null.

Since it is a pointer, it can be made to point to anything of the right type. Since it is a pointer to A, it can point to any object instance of class A.  It can also point to a special value meaning "this does not point to anything", called NULL, nullptr, or 0. 

You can point it to anything of the right type that makes sense for your program.  You might set it to point to testObject1 since it is an instance of class A.  You could set it to point to testObjA or test_1 since those are also instances of class A. You could create an object with new, and point to the newly created object.

You can even make data members that point to the object itself. In fact, one of these is automatically created for you, called "this". 

There is an important detail for whatever pointers you use.  Pointers point to things.  If you point to something and then the thing gets destroyed, the pointer will continue to point to that spot.  Even though the pointer still points there, once the object is destroyed the pointer is pointing to dead memory.  If you try to use the pointer after something is destroyed your program will misbehave, and probably crash.

Which brings us to:

Is this good or bad for what reasons?

 It is not good or bad.  What really matters is that pointers point to things that actually exist or they be changed to point to a null value (NULL, nullptr, or 0).  Controlling the lifetime of objects is an important part of programming and one of the most common reasons for programs to crash.  Smart pointers can help with this, but ultimately the decision is up to the programmer to do whatever works for the program they're building.

Share this post


Link to post
Share on other sites

Regular pointers are okay. You just have to remember to call delete on them at the appropriate time.
 

 

Not dynamically allocated pointers with new, regular pointers:

 

class A{

 

 

}

class B{

 

//'regular' maybe its called raw instead?

A * object;

}

 

I do enjoy the terminology, sincerely,

 

Josheir 

Share this post


Link to post
Share on other sites

Not dynamically allocated pointers with new, regular pointers:

Pointers can point to anything of the right type. You can point it to an existing object of that type, you can create an object dynamically with new and have it point to the new instance (and eventually call delete on that object), you can point it to NULL or nullptr or 0 to say it doesn't point at any object.

Share this post


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

Sign in to follow this  

  • Advertisement