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

Pages: 1234
closed account (4z0M4iN6)
Three days ago I discovered a bug in my programm, which was not so easy to find.

I often used a self defined class "AllocMemory" for allocating memory and all worked well.

So I wrote:

AllocMemory Allocation(length);

or also

AllocMemory Allocation = AllocMemory(length);

and following accesses e.g. via memcpy worked without problems.

But three days ago I had written:

1
2
3
4
5
6
7
8
9
10
AllocMemory Allocation

if(condition)
{
    Allocation = AllocMemory(length);
    ...
    memcopy(...,...,...);
    ....
}
....

And the program hanged, when it reached memcpy.

So I thought I'am a newsbie with only half a year programming experience in C++ and 3 weeks in Visual Studio 2008 C++ and I don't know much about the finesses of programming in C++. And intuitively I corrected my mistake by writing:

1
2
3
4
5
6
7
8
9
10
11
12
13
AllocMemory Allocation

if(condition)
{
    AllocMemory TempAllocation(length);
    Allocation = TempAllocation;
    TempAllocation.DontFree(); // I had such a function

    ...
    memcopy(...,...,...);
    ....
}
....

And all worked well.

The day after, we had a holiday in Germany, and I was in an amusement park with family. But after some time I began to think about this programming problem. And what I thought then, was:

No it's not me who is the newbie. Maybe all others, who are programming in Visual Studio 2008 C++ are newbies. Most of them, because they don't know about the basics of Visual Studio 2008 C++ and some, because they don't know about programming generally in C++.

Yesterday I made some tests, and now I know exactly about the basics of programming in Visual Studio 2008 C++.


You know the basics of C++. So you know how instanciate an object:

It's this for example:

CatClass CatFrisky("Frisky");

or

CatClass CatTom = CatClass("Tom");

And I tested this and can tell you, that this not only works correct in normal C++, but also in this C++ of Microsoft.

Do you think, that operations like assignments work very different from normal C++. If you dont think so, then you are a newbie.

What do you think about:

MyCat = CatFrisky;

What will be the outcome, if you write such a line without knowing the basics?


The first Basic

Before you assign a new content to an existing object, you first have to destruct the old content.



Correct would be:

1
2
MyCat.~CatClass();
MyCat = CatFrisky;


But if you think, not you are the newbie, but some others, you also can write:

1
2
3
4
5
// work around for:
// MyCat = CatFrisky;

MyCat.~CatClass(); // MS forgot this
MyCat = CatFrisky;


The second basic is a very interesting one.

Do you know what happens, if you write:

CatClass("UnusedCat");

Don't worry, it also works correct with visual studio 2008. But it's important, that you know this.

What happens, is something like this:

1
2
CatClass * pTemp = new CatClass("UnusedCat");
delete pTemp; 


If you know this, can you imagine, what would happen, if you write the following, and why you shouldn't write it:

 
MyCat = CatClass("UsedCat");


So please consider the second basic.


The second Basic

Consider the difference between an used and an unused object.
If you assign a new instanciated temporary object to an existing object, don't delete the temporary object, but only free the memory.


So instead of:

MyCat = CatClass("UsedCat");

write:

1
2
3
4
5
6
7
// Work around for:
// MyCat = CatClass("Frisky")

MyCat.~CatClass(); // MS forgot this
CatClass * pTemp = new CatClass("Frisky");
MyCat = * pTemp;
free(pTemp); // MS used delete 


It's a pity that you can't use this work around also for a function, which returns an object, like:

CatClass CatFrisky = ReturnCat("Frisky");

Oh, sorry for causing worries. No, no, this works well.

But I ment:

CatFrisky = ReturnCat("Frisky");

Do you wonder, how this problem could be solved? Then you should know about the third basic.


The third Basic

Use classes, which can disable destruction.



Istead of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CatClass {

public:
   char * Name;
 
   CatClass(char * name = "UnnamedCat")
   {
      Name = name;
   }
   

   virtual ~CatClass()
   {
      Name = "DestructedCat";
   }
};


use:

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
class CatClass {

private:

   bool DestructionEnabled;

public:

   char * Name;
 
   CatClass(char * name = "UnnamedCat", bool destructenable = true)
   {
      Name = name;
      DestructionEnabled = destructionenable;
   }
   
   void EnableDestruction()
   {
       DestructionEnabled = true;
   }

   virtual ~CatClass()
   {
      if(DestructionEnabled)
      {
          Name = "DestructedCat";
      }
   }
};


Now it's not a problem to write the correct return function:

1
2
3
4
5
6
#define DONTDESTRUCT false

CatClass ReturnCat(char * name = "Unnamed Cat")
{
   return CatClass(name,DONTDESTRUCT);
}


And it's also easy now to write the correct work around:


1
2
3
4
5
6
// work around for:
// MyCat = ReturnCat("Frisky");

MyCat.~CatClass(); 
MyCat = ReturnCat("Frisky");
MyCat.EnableDestruction();


Now you have much learned about programming in Visual 2008 C++ and you no longer are a newbie.

Maybe I helped you and now you also could help me?


My Question is:

Is there a bug fix for this behavior of the C++ compiler for Visual Studio 2008 Professional. It's only some months ago, when I installed this VS and there were a lot of updates, but seemingly no one which met my expectations.
Maybe this was fixed for Visuasl Studio 2010 or 2011. But we wouldn't like to buy new professional versions.




Thank you in advance

Your dadabe
Last edited on
The problem you're faced with is likely the 'shallow' vs 'deep' copy.

'shallow' means that you only copy the pointer to another object. If you free that pointer in the other object the first object is in trouble because it thinks it points to a valid memory, but doesn't

Instead of copying the data external like you did overwrite the operator=() and do it there.

By the way it hasn't much to do with Visual Studio 2008 just plain C++.
closed account (4z0M4iN6)
I know, what you mean, but it was not a problem like shallow and deep, the pointers were all ok. Because they were copied, before the temporary object was destructed. The destruction was not ok, because it freed my allocated memory.

I can show you the outcome of my test and the code.

The Outcome:


================= Case 1 ==================
CompilerTest Object1;
Object1.WhatAreYou("Object1");
===========================================
Initializing NewObject
Me Object1 is Initialized NewObject
===========================================

================= Case 2 ==================
CompilerTest Object2("Object2");
Object2.WhatAreYou("Object2");
===========================================
Initializing Object2
Me Object2 is Initialized Object2
===========================================

================= Case 3 ==================
CompilerTest Object3 = CompilerTest("Object3");
Object3.WhatAreYou("Object3");
===========================================
Initializing Object3
Me Object3 is Initialized Object3
===========================================

================= Case 4: 1 Error ==========
CompilerTest Object4;
Object4.WhatAreYou("Object4");
Object4 = Object2;
Object4.WhatAreYou("Object4");
===========================================
Initializing NewObject
Me Object4 is Initialized NewObject
Me Object4 is Initialized Object2
===========================================

================= Case 5: 2 Errors ===========
CompilerTest Object5;
Object5.WhatAreYou("Object5");
Object5 = CompilerTest("UsedObject");
Object5.WhatAreYou("Object5");
===========================================
Initializing NewObject
Me Object5 is Initialized NewObject
Initializing UsedObject
Destructing UsedObject
Me Object5 is Initialized UsedObject
===========================================

================= Case 6: ===============
CompilerTest Object6 = ReturnCompilerTest("UsedReturnObject1");
Object6.WhatAreYou("Object6");
===========================================
Initializing UsedReturnObject1
Me Object6 is Initialized UsedReturnObject1
===========================================

================= Case 7: 2 Errors ===========
CompilerTest Object7;
Object7.WhatAreYou("Object7");
Object7 = ReturnCompilerTest("UsedReturnObject2");
Object7.WhatAreYou("Object7");
===========================================
Initializing NewObject
Me Object7 is Initialized NewObject
Initializing UsedReturnObject2
Destructing UsedReturnObject2
Me Object7 is Initialized UsedReturnObject2
===========================================

Destructing UsedReturnObject2
Destructing UsedReturnObject1
Destructing UsedObject
Destructing Object2
Destructing Object3
Destructing Object2
Destructing NewObject




The Code:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// compilertest.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"
#include <conio.h>


class CompilerTest {

public:

	char * Object;
	char * State;

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



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

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


CompilerTest ReturnCompilerTest(char * Name)
{
	return CompilerTest(Name);
}

int _tmain(int argc, _TCHAR* argv[])
{

	// Case 1 -----------------------------------
	printf("================= Case 1 ==================\n");
	printf("CompilerTest Object1;\n");
	printf("Object1.WhatAreYou(\"Object1\");\n");
	printf("===========================================\n");

	CompilerTest Object1;
	Object1.WhatAreYou("Object1");

	printf("===========================================\n\n");


	// Case 2 -----------------------------------
	printf("================= Case 2 ==================\n");
	printf("CompilerTest Object2(\"Object2\");\n");
	printf("Object2.WhatAreYou(\"Object2\");\n");
	printf("===========================================\n");

	CompilerTest Object2("Object2");
	Object2.WhatAreYou("Object2");

	printf("===========================================\n\n");


	// Case 3 -----------------------------------
	printf("================= Case 3 ==================\n");
	printf("CompilerTest Object3 = CompilerTest(\"Object3\");\n");
	printf("Object3.WhatAreYou(\"Object3\");\n");
	printf("===========================================\n");

	CompilerTest Object3 = CompilerTest("Object3");
	Object3.WhatAreYou("Object3");

	printf("===========================================\n\n");

	// Case 4 ---------------------------------------------
	printf("================= Case 4: 1 Error ==========\n");
	printf("CompilerTest Object4;\n");
	printf("Object4.WhatAreYou(\"Object4\");\n");
	printf("Object4 = Object2;\n");
	printf("Object4.WhatAreYou(\"Object4\");\n");
	printf("===========================================\n");

	CompilerTest Object4;
	Object4.WhatAreYou("Object4");
	Object4 = Object2;
	Object4.WhatAreYou("Object4");

	printf("===========================================\n\n");


	// Case 5 -----------------------------------
	printf("================= Case 5: 2 Errors ===========\n");
	printf("CompilerTest Object5;\n");
	printf("Object5.WhatAreYou(\"Object5\");\n");
	printf("Object5 = CompilerTest(\"UsedObject\");\n");
	printf("Object5.WhatAreYou(\"Object5\");\n");
	printf("===========================================\n");

	CompilerTest Object5;
	Object5.WhatAreYou("Object5");
	Object5 = CompilerTest("UsedObject");
	Object5.WhatAreYou("Object5");

	printf("===========================================\n\n");


	// Case 6 -----------------------------------
	printf("================= Case 6: ===============\n");
	printf("CompilerTest Object6 = ReturnCompilerTest(\"UsedReturnObject1\");\n");
	printf("Object6.WhatAreYou(\"Object6\");\n");
	printf("===========================================\n");

	CompilerTest Object6 = ReturnCompilerTest("UsedReturnObject1");
	Object6.WhatAreYou("Object6");

	printf("===========================================\n\n");


	// Case 7 -----------------------------------
	printf("================= Case 7: 2 Errors ===========\n");
	printf("CompilerTest Object7;\n");
	printf("Object7.WhatAreYou(\"Object7\");\n");
	printf("Object7 = ReturnCompilerTest(\"UsedReturnObject2\");\n");
	printf("Object7.WhatAreYou(\"Object7\");\n");
	printf("===========================================\n");

	CompilerTest Object7;
	Object7.WhatAreYou("Object7");
	Object7 = ReturnCompilerTest("UsedReturnObject2");
	Object7.WhatAreYou("Object7");

	printf("===========================================\n\n");

	_getch();
	return 0;
}




You can see:

destruction of the old content missed in cases 4, 5 and 7

destructing the temporary object in cases 5 and 7 (must not happen)

I know: difficult to believe


Last edited on
It looks like you have not overloaded the copy constructor and copy assignment operator correctly or not at all.

Normally you should never have to call the destructor explicitly unless you have created the object with placement new.
Last edited on
The first Basic

Before you assign a new content to an existing object, you first have to destruct the old content.

Correct would be:

MyCat.~CatClass();
MyCat = CatFrisky;


It is totally invalid statement!

The second Basic

Consider the difference between an used and an unused object.
If you assign a new instanciated temporary object to an existing object, don't delete the temporary object, but only free the memory.



It is one more fulishness!

Last edited on
closed account (4z0M4iN6)
Hello Peter87,

why should I have to overload a standard copy operator for such a basic thing, like:

MyCat = CatFrisky;

And what for? For copying an unitialized allocated memory?
The pointer for the allocated memory was copied correctly.
And do you think, overloading the copy operator will hinder the compiler to call the destructor later afterwards.


And vlad,

did you try: MyCat.~CatClass()

It's a valid statement. The compiler offers it, the compiler translates it and the compiler performs it.
Last edited on
dadabe wrote:
why should I have to overload a standard copy operator for such a basic thing, like:
MyCat = CatFrisky;

It depends on what you want it to do. If the object contains a pointer the default copy constructor/operator will just copy the pointer and not the actual data pointed to. In that case you probably want to make an overload so that MyCat can own it's own copy of the data.

dadabe wrote:
did you try: MyCat.~CatClass()
It's a valid statement. The compiler offers it, the compiler translates it and the compiler performs it.

No it's not valid. I quote the standard:
Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8). [ Example: if the destructor for an automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined.
why should I have to overload a standard copy operator for such a basic thing, like:

MyCat = CatFrisky;


Because sometimes the default copy does not do what you want. Here is a common example:

1
2
3
4
5
class Image
{
  int* pointerToTheImageData;
  string filename;
};


When the Image object is created, the constructor uses the filename to read in data, and allocates enough memory dynamically to hold all the data. What happens now if you copy the image object? You get two image objects, both of which point to the same image data (so changing the data in one image object changes the data in the other as well). If the coder wants to be able to treat the two image objects as independent objects, there will need to be a copy operator written that allocates more memory, copies all the data to it, and then sets the pointer in the new image object to point to that copied set of data.

The first case is known as "shallow" copying. The second is commonly known as "deep" copying.

To summarise:
why should I have to overload a standard copy operator for such a basic thing, like:
MyCat = CatFrisky;

Because the compiler is not psychic and cannot possible know exactly what you want to happen with a copy in all but the most simple of cases.
Last edited on
closed account (4z0M4iN6)
Yes Moschops you are right, that sometimes we need to overload the copy operator.

But in this case not, because I only needed the pointer to the allocated memory. And if you have a look at my compiler test code, you see the copying works well. It's a little bit tricky, because I trusted in this example on static allocated strings.

And copying a pointer in my AllocMemory class was much simpler. I needed only the pointer und an error flag.

Yes vlad, you are right, explicitly invoking the destructor is a little bit non standard. But the lifetime of the object don't end, because a new assignment follows.

If you know a better standard compatible destruction, let me know.
Or better we should write a second destruct function, which we may call without worry, because it's not the destructor?

I think of:

1
2
3
4
5
6
7
8
9
10
11
void finalize()
{
....
....
}


~CatClass()
{
     finalize();
}


Then it would be ok to call MyCat.finalize();
Last edited on
Yes vlad, you are right, explicitly invoking the destructor is a little bit non standard.

It's not "a little bit non-standard", it's wrong. When MyCat is an automatic object, it will be destroyed at the end of the scope, so you'd end with a double destruction. What you could do is to create a new instance at the same location using placement new. But that would be nonsense. What you really want is a simple assignment.

But the lifetime of the object don't end, because a new assingment follows.

You cannot assign something to an object that is already destroyed. It's undefined behavior.

If you know a better standard compatible destruction, let me know.

Do not call the destructor yourself. Automatic objects are destroyed automatically at the end of scope and objects allocated with new are destroyed when using delete.

Then it would be ok to call MyCat.finalize();

That's certainly possible, the question is: what purpose does that serve? Why do you need such a function?

Also, you cannot mix new and free. You may only use free on a pointer returned by malloc - likewise, you may only use delete on a pointer returned by new.
Last edited on
I'm getting the feeling that this is an attempt to force the programming style of some other language entirely onto C++.
closed account (4z0M4iN6)
Do not call the destructor yourself. Automatic objects are destroyed automatically at the end of scope and objects allocated with new are destroyed when using delete.


Ok Athar, then I should suggest to use a function maybe with name "finalize"
I'm getting the feeling that this is an attempt to force the programming style of some other language entirely onto C++.

Such as C. Spotting the name "Frisky", it sounds like OP is using one of J. Liberty's books (C++ in 21 days or C++ in 24 hours), which teaches horrible C with classes and are riddled with errors.
Last edited on
Ok Athar, then I should suggest to use a function maybe with name "finalize"

Again, why do you need such a function? If you want a function that returns the object to a default-constructed state, reset() would be a more appropriate name. Of course, when operator= is implemented properly, you can always do Frisky=CatClass(); to achieve the same.
Last edited on
closed account (4z0M4iN6)
Again, why do you need such a function? If you want a function that returns the object to a default-constructed state, reset() would be a more appropriate name.

Because if I have allocated memory, the content of the destructor function has to be executed.

An old allocation has to be freed before a new allocation is done. OK reset maybe would be a more appropriate name.

But if I would use another compiler like gcc. Would this compiler not automatically call the destructor function?
Last edited on
The day after, we had a holiday in Germany,[...]

No it's not me who is the newbie. Maybe all others, who are programming in Visual Studio 2008 C++ are newbies. Most of them, because they don't know about the basics of Visual Studio 2008 C++ and some, because they don't know about programming generally in C++.


I like how a single visit to Germany turned humility into arrogance.

Generally speaking, "I'm not wrong, everyone else is!" is the moment where you should step back and think about what you're doing.

As people above said, the problem was shallow vs deep copy. You were copying pointers to data, but the data itself was destructed.

Also, none of this is "basic C++"; it's bad C.
Because if i have allocated memory, the content of the destructor function has to be executed.

But that code belongs into the destructor.

An old allocation has to be freed before a new allocation is done.

It's unclear what you mean. And just for the record: when you use operator= to reassign an object, no object is destroyed and none is created, just the members are being copied.
Last edited on
the name of the function doesn't determine what it does, the contents do. You could call your function Chicken() and then put code in it to return the object to a default-constructed state. Athar was suggesting a more logical name.
closed account (4z0M4iN6)
It's unclear what you mean. And just for the record: when you use operator= to reassign an object, no object is destroyed and none is created, just the members are being copied.

Maybe you are right and I don't know so much about the correct behavior of C++ compilers. It seems I only expected too much. But I will try gcc and then I can see, what this compiler does.

But with the other bug I think I am right. Nobody call tell me, that it is a correct behavior of a compiler, if it destroys the object, before I can use it.

The pointers are copied very well, which you can see in test case 5. But if I would have allocated memory, the memory would have been freed.
The pointers are copied very well, which you can see in test case 5. But if I would have allocated memory, the memory would have been freed.

Yes, the pointers are copied. What they point to is not copied. So if the destructor of the temporary uses delete, Object5 will be left with a dangling pointer. That's exactly the issue with shallow vs deep copying.

But with the other bug I think I am right. Nobody call tell me, that it is a correct behavior of a compiler, if it destroys the object, before I can use it.

What other bug? Elaborate.
Pages: 1234