if you'd used a shared_ptr |
"Calling the destructor is never correct." |
You cannot assign something to an object that is already destroyed. It's undefined behavior. |
MyCat = CatFrisy;
the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined. (ISO C++) |
CatClass("Frisky");
MyCat = CatClass("Frisky");
MyCat = ModifyCat(MyCat);
I cannot assign something to an object at all |
I cannot assign a Cat to another Cat. |
I only can assign something to a storage. |
And in the relevant cases you have to destroy the old object. Otherwise you will get memory leaks. And exactly therefore an explicit destructor call is neccessary. And don't mix it up with an implicite destructor call: an object should not call it's own destructor, this could lead to undefined behavior. |
s not a temporary. |
A temporary would be neccessary in this case |
And this temporary has to be destroyed automatically by the compiler in the same line. |
And which cat should be destoyed: the old or the new cat? And you in earnest want to tell me, it should be the new cat? |
dadabe wrote: |
---|
"I cannot assign something to an object at all. [...] I only can assign something to a storage." |
dadabe wrote: |
---|
"And wether destruction is neccessary, depends, how the storage was used before" |
dadabe wrote: |
---|
"is a temporary, because the lifetime of the object ends at the end of the expression." |
"Is not a temporary and the lifetime of the object begins here." |
dadabe wrote: |
---|
"And this temporary has to be destroyed automatically by the compiler in the same line." |
I cannot assign something to an object at all. [...] I only can assign something to a storage. |
The destructor of an object is always (except placement new) invoked - irrespective of their use. |
however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior. |
Of course CatClass() is a temporary. Assuming MyCat is an object, CatClass::operator = will be invoked, which copies the data from temporary. When the assignment is made, the temporary object is destroyed. MyCat is in now way bound to the temporary object. |
dadabe wrote: |
---|
"If somebody says "always" or "never" I doubt it, whatever he says. Nobody can know all, and so can't know about ever or never." |
dadabe wrote: |
---|
"If an object has a pointer to allocated memory and I replace this object by another object, the compiler will not implicitly invoke the destructor." |
dadabe wrote: |
---|
"So: if you forget "delete" or "~MyClass()"" |
dadabe wrote: |
---|
"C was easy: the compiler never will do it" |
dadabe wrote: |
---|
"but not a final temporary, only a temporary temporary." |
dadabe wrote: |
---|
"And when the assignment is made, the storage of the temporary has to be released, but without calling the destructor." |
"And when the assignment is made, the storage of the temporary has to be released, but without calling the destructor." |
CatClass MyCat = CatClass("Frisky");
|
|
dadabe wrote: |
---|
CatClass MyCat = CatClass("Frisky"); |
|
|
Here's how I see it: First, the anonymous CatClass object is pushed onto the stack. The CatClass::CatClass(char*) (or similar) constructor is invoked. Then, MyCat is pushed onto the stack. The copy constructor of MyCat is invoked by passing the anonymous CatClass object to it. After MyCat is fully constructed, the destructor of the anonymous CatClass object is invoked, which is then popped from the stack. At this point, MyCat is an exact copy of the anonymous CatClass object |
CatClass MyCat = CatClass("Frisky");
CatClass MyCat("Frisky");
The form of initialization (using parentheses or =) is generally insignificant |
First, the anonymous CatClass object is pushed onto the stack. |
The CatClass::CatClass(char*) (or similar) constructor is invoked. |
Then, MyCat is pushed onto the stack. |
The copy constructor of MyCat is invoked by passing the anonymous CatClass object to it. |
After MyCat is fully constructed, the destructor of the anonymous CatClass object is invoked, which is then popped from the stack. |
At this point, MyCat is an exact copy of the anonymous CatClass object |
MyCat = CatClass("Frisky");
CatClass MyCat("Frisky");
CatClass MyCat = CatClass("Frisky");
CatClass MyCat = "Frisky";
MyCat = "Frisky";
dadabe wrote: |
---|
"But the copy-intialization isn't:" [quote=dadabe]"No, the storage for the block variable MyCat has to be reserved first on the stack" |
dadabe wrote: |
---|
"After destruction, the pointers to allocated memory could have been set to null for example." |
dadabe wrote: |
---|
"But the memory is deallocated (freed or deleted). And MyCat has dangling pointers to not any longer allocated memory." |
CatClass MyCat = CatClass("Frisky");
[/quote]CatClass MyCat(CatClass("Frisky"))
.dadabe wrote: |
---|
"Ok, it worked, but the copy-initialization is just:"CatClass MyCat = "Frisky"; |
dadabe wrote: |
---|
MyCat = "Frisky"; "I just have to code an assignment operator for this case." |
I didn't realise I wrote that. |
First, the anonymous CatClass object is pushed onto the stack. |
Then, MyCat is pushed onto the stack. |
No, the storage for the block variable MyCat has to be reserved first on the stack, so that temporary values can be pushed and popped, without popping the block variable. |
What pointers? |
AllocMemory Allocation(length);
|
|
|
|
CatClass MyCat = CatClass("Frisky");
That, too, invokes the copy constructor |
(I extented my test und printed the address where the pointer "this" pointed to, during and after construction: it was the same address. And I also added a copy constructor and an assignment operator and saw, none of them was invoked.) |
================= Case 3 ================== CompilerTest Object3 = CompilerTest("Object3"); Object3.WhatAreYou("Object3"); =========================================== Initializing Object3 Address of Object3: 0x0025FED0 Me Object3 is Initialized Object3 Address of Object3: 0x0025FED0 =========================================== |
|
|
I just have to code an assignment operator for this case. Did I get it right now? |
================= Case 9: 0 Errors =========== CompilerTest Object9(CompilerTest("Object9")); Object9.WhatAreYou("Object9"); =========================================== Initializing Object9 Address of Object9: 0x0015FB90 Me Object9 is Initialized Object9 Address of Object9: 0x0015FB90 =========================================== |
|
|
================= Case 4: ================= CompilerTest Object4; Object4.WhatAreYou("Object4"); Object4 = Object2; Object4.WhatAreYou("Object4"); =========================================== Initializing NewObject Address of NewObject: 0xDB7B9F00 Me Object4 is Initialized NewObject Address of NewObject: 0xDB7B9F00 Copy Assignment Operator is invoked Me Object4 is Initialized Object2 Address of Object2: 0xDB7B9F00 =========================================== ================= Case 5: ================= CompilerTest Object5; Object5.WhatAreYou("Object5"); Object5 = CompilerTest("UsedObject"); Object5.WhatAreYou("Object5"); =========================================== Initializing NewObject Address of NewObject: 0xDB7B9EF0 Me Object5 is Initialized NewObject Address of NewObject: 0xDB7B9EF0 Initializing UsedObject Address of UsedObject: 0xDB7B9F40 Move Assignment Operator is invoked Destructing Non destructable Temporary Me Object5 is Initialized UsedObject Address of UsedObject: 0xDB7B9EF0 =========================================== .... ================= Case 7: ================== CompilerTest Object7; Object7.WhatAreYou("Object7"); Object7 = ReturnCompilerTest("UsedReturnObject2"); Object7.WhatAreYou("Object7"); =========================================== Initializing NewObject Address of NewObject: 0xDB7B9ED0 Me Object7 is Initialized NewObject Address of NewObject: 0xDB7B9ED0 Initializing UsedReturnObject2 Address of UsedReturnObject2: 0xDB7B9F50 Move Assignment Operator is invoked Destructing Non destructable Temporary Me Object7 is Initialized UsedReturnObject2 Address of UsedReturnObject2: 0xDB7B9ED0 =========================================== |
Object10 = CompilerTest("UsedObject") = Object2;
================= Case 10: ================= CompilerTest Object10; Object10.WhatAreYou("Object10"); Object10 = CompilerTest("UsedObject") = Object2 Object10.WhatAreYou("Object10"); =========================================== Initializing NewObject Address of NewObject: 0x556874B0 Me Object10 is Initialized NewObject Address of NewObject: 0x556874B0 Initializing UsedObject Address of UsedObject: 0x55687570 Copy Assignment Operator is invoked Copy Assignment Operator is invoked Destructing Object2 Me Object10 is Initialized Object2 Address of Object2: 0x556874B0 =========================================== |
Bug? Yes. But for linux g++. About VC11 I don't know. |
Object2
is an lvalue, the compiler will not implicitly move assign lvalues (because it could very easily break older code where moves were not supported). Because of this it is copy assigned as is the temporary object. Change Object2
to an Rvalue (temporary), you will notice it now is move assigned, which is then copy assigned to the lvalue Object10
. If you want this entire expression to be "moved" into Object10 then you need to use std::move on the temporary move assignment, because your compiler is not doing this implicitly (as mine does not either). For good reason too, it