Are these really the basics of programming in C++ with visual studio 2008?

Pages: 1234
I think I must be missing the big deal. If you want a certain behavior, use objects which guarantee that behavior.

For instance, if you'd used a shared_ptr to hold the address of your allocated memory in the AllocMemory class, you would've had no problems at all with the class as far as temporary construction and destruction went. With C++11 you could choose between unique_ptr and shared_ptr, depending on the exact behavior you want.

Automatically defined move constructors and assignment operators wouldn't solve your problem any more than the default copy constructor and copy assignment operators did. They treat raw pointers in exactly the same way automatically defined copy constructors and copy assignment operators do, because that is the right thing to do for raw pointers.
closed account (4z0M4iN6)
cire wrote:
if you'd used a shared_ptr


Oh, I didn't know about such new things. It looks like using C++/CLI. C++/CLI works very fine.

And it's Standard Template Library.

Very nice!!!
Last edited on
closed account (4z0M4iN6)
Gaminic wrote:
"Calling the destructor is never correct."


Please read the following post
Last edited on
closed account (4z0M4iN6)
Athar wrote:
You cannot assign something to an object that is already destroyed. It's undefined behavior.

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 if I write:

MyCat = CatFrisy;

This means:

Copy the object inside the storage CatFrisky to the storage MyCat.

And wether destruction is neccessary, depends, how the storage was used before, whether there is a cat inside, wether it is a shallow or deep copied cat or maybe a nullcat.

In some cases the old object has to be destroyed before a replacement may be done.

The compiler can't know, whether or not.
Only you can know.

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. But:
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++)

And this isn't something new, but is standard since beginning of C++.

After this you can copy, because the storage isn't destroyed, only the object, which it contained before.

And about temporary:

CatClass("Frisky");

is a temporary, because the lifetime of the object ends at the end of the expression.

But:

MyCat = CatClass("Frisky");

Is not a temporary and the lifetime of the object begins here.
(I am shure, but couldn't find the proof yet - maybe ISO C++ didn't have an idea that people could think, the lifetime ends here - maybe the following example could be the proof?)

A temporary would be neccessary in this case, says ISO-C++:

MyCat = ModifyCat(MyCat);

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?

It is a bug!!!
Last edited on
I cannot assign something to an object at all


Wrong.

I cannot assign a Cat to another Cat.


Wrong.

I only can assign something to a storage.


But a storage in this context is my house. You can't assign stuff my house. Don't be stupid.

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.


So why aren't you following your own advice?

s not a temporary.


MyCat is not. The temporary however, is. Obviously.

A temporary would be neccessary in this case


Indeed.

And this temporary has to be destroyed automatically by the compiler in the same line.


Yes.

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?


The temporary, duh. There is no "old" or "new" cat, there is MyCat and a temporary.

But yeah, I really think this is spoonlicker again. No one could be that dumb without trying.

closed account (zb0S216C)
dadabe wrote:
"I cannot assign something to an object at all. [...] I only can assign something to a storage."

False. An object (an instance of a class) is a piece of storage.

dadabe wrote:
"And wether destruction is neccessary, depends, how the storage was used before"

The destructor of an object is always (except placement new) invoked - irrespective of their use.

dadabe wrote:
"is a temporary, because the lifetime of the object ends at the end of the expression."

True, but if a reference claims it, its lifetime matches that of the reference that claimed it.

"Is not a temporary and the lifetime of the object begins here."

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:
"And this temporary has to be destroyed automatically by the compiler in the same line."

Same as above.

Wazzak
Last edited on
closed account (4z0M4iN6)
I cannot assign something to an object at all. [...] I only can assign something to a storage.

Sorry, I was wrong. Long years of Assembly and C and new in C++.
An assignment was: copy an amout of bytes at one storage location to another storage location.

But C++: call the assignment operator function of the object refenced at the left side of the assignment operator with the rvalue at the right side of the assignment operator. And this is a very big difference.

Framework wrote:
The destructor of an object is always (except placement new) invoked - irrespective of their use.

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.

And a compiler only calls the destructor implicit in well defined cases and not dependent of usage.

If I reuse the storage location of an object for another object, I must think about destruction. For example:

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. I first thought, this would be a compiler bug. But it's not a bug, because the compiler can't no nothing, about the sense of my object. So ISO C++:

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.

So: if you forget "delete" or "~MyClass()" (should only be done in certain cases - where it has to be done and where the compiler will not do it implicitly - the compiler will not correct, what you forgot or maybe not?

C was easy: the compiler never will do it
But it was simple: have to know not much - a small booklet is enough
But it caused much bugs: people forgot it.

C++ is more secure: the compiler does it
But it's not simple: have to know much - the cases, where the compiler will do it implicitly and the cases where not - the C++ reference counts more than 1300 pages
And it causes also bugs: people don't now all cases.
Last edited on
closed account (4z0M4iN6)
Framework wrote:
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.

You are right about the temporary, the operator= and the destruction.

CatClass() is a temporary, but not a final temporary, only a temporary temporary.
You may use the operator=.
And when the assignment is made, the storage of the temporary temporary - it was a new object during construction - has to be released, but without calling the destructor.

temporary temporary: during construction an object may have another storage location as after
Last edited on
closed account (zb0S216C)
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."

The destructor of every automatic object is invoked at the end of the stack-frame in which it's defined. This is guaranteed. However, this is not the case with placement new. With placement new, the constructor and destructor must be invoked explicitly by the programmer; the compiler will not call neither implicitly.

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."

Of course. A well-designed class would handle the memory appropriately in both assignment and destructor functions. Remember, allocated memory becomes your responsibility, not the compiler's.

dadabe wrote:
"So: if you forget "delete" or "~MyClass()""

No, there's no or here. delete/delete[] invokes the destructor of the object(s) then frees the allocated memory. Forgetting either causes a leak.

dadabe wrote:
"C was easy: the compiler never will do it"

There's neither destructor nor constructor in C, if that's what you meant.

dadabe wrote:
"but not a final temporary, only a temporary temporary."

What?

dadabe wrote:
"And when the assignment is made, the storage of the temporary has to be released, but without calling the destructor."

That's preposterous. Failure to invoke the destructor will cause a leak, regardless. As I've said, the destructor of automatic objects is invoked automatically implicitly. Though, what you're saying is ambiguous. Are you referring to allocated memory, or automatic objects?

Wazzak
Last edited on
closed account (4z0M4iN6)
Thank you, Framework

I will explain something more about:

Me wrote:
"And when the assignment is made, the storage of the temporary has to be released, but without calling the destructor."

I thought of this:

CatClass MyCat = CatClass("Frisky");

There are two ways for implementation:

- allocate memory for the object, construction of the new object directly at the storage location, which is referenced by MyCat
- allocate memory for the object MyCat, construction of the new object at another storage location: allocate the memory for this temporary storage location, call the constructor, copy the structure to the storage location referenced by MyCat (default copy constructor), update pointer "this" and release the allocated memory for the temporary location.

ISO C++ says also, that there could be another storage location during construction time. So don't use the pointer "this" for external reference purposes.

The second way is also allowed. Und it's not a temporary. It's the new object MyCat which was at another storage location at construction time. And calling the destructor for the "storage location during destruction time" would be very foolish, BECAUSE THIS WOULD DELETE THE NEW MYCAT. Not the data of the structure, but data in allocated memory.

Exactly the same should be implemented in this case:

MyCat = CatClass("Frisky")

The difference is: the storage is already allocated. And here the new object has to be constructed at another storage location, because you can then use your user defined assignment operator= - maybe for destructing first the old object occupied by storage location MyCat - I don't know what you would like to do.

And it's not a temporary cat. It's the new cat, which should not be destroyed at the time, where it's live shall begin.

I can test gcc on monday. Or has someone linux installed and could try my test. I would like to know about the outcome.
closed account (4z0M4iN6)
Sorry I think, I got now the point.

The assignment operator is normally used for an existing object.
So you would do a deep copy for an existing object, because who can know, that this would be another case. And then you would allocate memory anew for a deep copy. And then we have a memory leak, if we don't destruct the temporary new created object.

I think I got it!

And I got also the correct implementation for my problem:

1
2
3
4
AllocMemory Allocation(condition ? 10000 : 0);

if(condition) ...
Last edited on
closed account (zb0S216C)
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.

All right, let's get this straight.

Consider this code:

1
2
3
CatClass NewCat(CatClass());

NewCat = CatClass();

OK, now, the first line can be written as follows:

1) The anonymous CatClass object is pushed onto the stack, and its default constructor is invoked.
2) When the anonymous CatClass object is fully constructed, NewCat is pushed on the stack, and its copy-constructor is invoked by passing the anonymous CatClass object to it.
3) Once NewCat's copy-constructor is finished, the anonymous CatClass's destructor is invoked, and is then popped from the stack. At this point, the anonymous object is gone, and NewCat is fully constructed.

The second line can also be written as follows:

1) The anonymous CatClass object is pushed onto the stack, and its default constructor is invoked.
2) CatClass::operator = does its thing.
3) Once the assignment has finished, the anonymous object's destructor is invoked, and it's then popped from the stack. Again, NewCat remains untouched by the anonymous object's destruction.

Wazzak
closed account (4z0M4iN6)
Framework wrote:

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


The example was:

CatClass MyCat = CatClass("Frisky");

This kind of initialization is called "copy-initialization".
We distinguish "copy-initialization" and "direct-initialization".

A "direct-initialization" is coded in this way:

CatClass MyCat("Frisky");

ISO C++ says:

The form of initialization (using parentheses or =) is generally insignificant


So Microsoft implemented the "copy-initialization" equal to a "direct-initialization". (I extented my test und printed the addresss 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.)

So, what happened, was:

A space of storage of size sizeof(CatClass) was reserved on the stack.
This was the storage for the object MyCat.
And then the constructor was called for MyCat.

This was all, what happened.

How you saw it, could also have been implemented.
But some details are not quite correct. And it would have produced a bug. Exactly the bug about which I wrote.


First, the anonymous CatClass object 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.

The CatClass::CatClass(char*) (or similar) constructor is invoked.

Quite correct.

Then, MyCat is pushed onto the stack.

As I already wrote: this would have been the first step.

The copy constructor of MyCat is invoked by passing the anonymous CatClass object to it.

Quite correct.

After MyCat is fully constructed, the destructor of the anonymous CatClass object is invoked, which is then popped from the stack.

This would be the big mistake. I will explain later.

At this point, MyCat is an exact copy of the anonymous CatClass object

And now you are under a misapprehension, because MyCat is an exact copy of the anonymous CatClass before the anonymous CatClass was destructed. After destruction, the pointers to allocated memory could have been set to null for example. But they are not set to null for MyCat. So all looks nice for MyCat, pointers, flags all ok. But the memory is deallocated (freed or deleted). And MyCat has dangling pointers to not any longer allocated memory.

Did you get this?

Because exactly this happens in the case:

MyCat = CatClass("Frisky");

And the fresh created object isn't copied, but wrong copied.
Last edited on
closed account (4z0M4iN6)
Sorry, now I saw my mistake - I red the specification more carefully. I'am just a beginner in C++, and it was me, who didn't understand the basics.

The direct-initialization is:

CatClass MyCat("Frisky");

But the copy-intialization isn't:

CatClass MyCat = CatClass("Frisky");

Ok, it worked, but the copy-initialization is just:

CatClass MyCat = "Frisky";

And if I want to do:

MyCat = "Frisky";

I just have to code an assignment operator for this case.

Did I get it right now?
closed account (zb0S216C)
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"

I didn't realise I wrote that.

dadabe wrote:
"After destruction, the pointers to allocated memory could have been set to null for example."

What pointers?

dadabe wrote:
"But the memory is deallocated (freed or deleted). And MyCat has dangling pointers to not any longer allocated memory."

What memory? Why are you adding information with each post?

CatClass MyCat = CatClass("Frisky");[/quote]
That, too, invokes the copy constructor, as well as: CatClass MyCat(CatClass("Frisky")).

dadabe wrote:
"Ok, it worked, but the copy-initialization is just:"

CatClass MyCat = "Frisky";

No copy constructor is invoked here. This would invoke the CatClass::CatClass(char*) (or similar) constructor.

dadabe wrote:
MyCat = "Frisky";

"I just have to code an assignment operator for this case."

Yes.

Wazzak

P.S: I'm done. I don't even know what this topic is about any more.
closed account (4z0M4iN6)
Framework wrote:

I didn't realise I wrote that.


You wrote:

First, the anonymous CatClass object is pushed onto the stack.


And you wrote (after another sentence):

Then, MyCat is pushed onto the stack.


And my answer (ment for these 2 sentences) was:

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.


Maybe you remember, what you wrote.

Framework wrote:
What pointers?

I thought, you got, what was the whole discussion about. It was about allocating memory, and what the reassigning had done. It was about:

AllocMemory Allocation(length);

Maybe you can imagine the constructor (it was not this simple memory, but let the following be the example):

1
2
3
4
5
AllocMemory(int size = 0)
{
      if(size) pMemory = new char[size];
      else size = null;
}


And you could imagine the destructor:

1
2
3
4
5
6
7
8
~AllocMemory()
{
     if(pMemory)
     {
          delete pMemory;
          pMemory = null;
     }
}


Cats also could have such pointers to allocated memory.

I wrote:

CatClass MyCat = CatClass("Frisky");

Framework wrote:
That, too, invokes the copy constructor


Which compiler did you use. Visual C++ (2008 and 2010 didn't invoke the copy constructor. As I wrote:

(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.)


Here is the outcome:

================= Case 3 ==================
CompilerTest Object3 = CompilerTest("Object3");
Object3.WhatAreYou("Object3");
===========================================
Initializing Object3
Address of Object3: 0x0025FED0
Me Object3 is Initialized Object3
Address of Object3: 0x0025FED0
===========================================


And here is the code of the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void PrintAddress(void * ptr,char * name)
{
	printf("Address of %s: 0x%0.8X\n",name,ptr);
}

class CompilerTest{

public:

	char * Object;
	char * State;

	CompilerTest(char * object = "NewObject")
	{
		Object = object;
		State = "Initialized";
		printf("Initializing %s\n",Object);
		PrintAddress((void *) this,Object);
	}

	void WhatAreYou(char * Name)
	{
		printf("Me %s is %s %s\n",Name,State,Object);
		PrintAddress((void *) this,Object);
	}

	~CompilerTest()
	{
		State = "Destructed";
		printf("Destructing %s\n",Object);
	}

	CompilerTest &operator=( CompilerTest &ptRHS )
	{
	   Object = ptRHS.Object;
	   State = ptRHS.State;
  	   printf("Copy Assignment Operator is invoked\n");

	   return *this;  // Assignment operator returns left side.
	}


	 // Copy constructor
	CompilerTest(const CompilerTest &ptRHS)
	{
	   Object = ptRHS.Object;
	   State = ptRHS.State;
	   printf("Copy Constructor is invoked\n");
	}

};


Maybe you understand now, what pointers and what memory?
Good it sounds like you finally understand, and the second post in here by coder777 answered the problem you were seeing all along.

I just have to code an assignment operator for this case.

Did I get it right now?

Correct.
closed account (4z0M4iN6)
Thank you clanmjc, and about your example Framework.

Now I know, how to do it correct.

Open is still, wether it's correct or not correct, what the compiler does if we do it, how I did first.

There are still wrong assumptions of other people.

Hi, Framework:

I tested your example. And here is the outcome:

no temporary, no destruction, no copy construction, no assignment, just direct initialization (same address during construction and after).


================= 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
===========================================



Last edited on
closed account (4z0M4iN6)
Problem partly solved with VC11 but maybe still a bug (linux g++) - also in VC11?

The problem is solved with the move assignment operator, available with VC11.

Consider this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	// copy assignment operator ----------------------
	CompilerTest &operator=(const CompilerTest & ptRHS )
	{
	   Object = ptRHS.Object;
	   State = ptRHS.State;
  	   printf("Copy Assignment Operator is invoked\n");

	   return *this;  // Assignment operator returns left side.
	}

	// move assignment operator ----------------------
	CompilerTest &operator=(CompilerTest && ptRHS )
	{
	   Object = ptRHS.Object;
	   State = ptRHS.State;
  	   printf("Move Assignment Operator is invoked\n");

	   // hinder the destructor to free the memory
	   ptRHS.Object = "Non destructable Temporary";
	   ptRHS.State = "deallocate memory disabled";

	   return *this;  // Assignment operator returns left side.
	}


Here the outcome:

================= 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
===========================================


Case 4 uses the copy assignment operator, because there's an existing object.
Cases 5 and 7 use the move assignment operator, because it's a temporary object.

And the outcome is very correct!

And now you think all is fine?
No, but who could find bugs?
I can, I am an expert in this.

What do you think about:

Object10 = CompilerTest("UsedObject") = Object2;

Here the outcome:


================= 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
===========================================


Object2 is copied to the temporary (correct).
And now the copy assignment operator is used for copying the temporary to Object10 (bug)!
And then the copy of Object2 is destructed.

Bug? Yes. But for linux g++. About VC11 I don't know.


And the first problem will not be solved with VC11, if not treated.
g++ behaved very correct. Didn't compile "Object5 = CompilerTest("Used Object)" . Said, there is no fitting assignment operator.

The correct behavior will be available estimatingly with VC14.

Why VC14?
VC 12 - too late for VC12
VC13 - bad luck
VC14!
Last edited on
Bug? Yes. But for linux g++. About VC11 I don't know.


What is the bug? 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 would could blow up legacy code.
Last edited on
Pages: 1234