problem with maps and pointer class

I'm making a pointer class. I'm aware there are existing ones, I have tried auto_ptr but really want to make my own that will work just how I want. I am having a compilation problem that dosn't seem to make that much sence.

Here is my code, the error is commented in ptr::~ptr.

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
#include <iostream>
#include <string>
#include <map>

//#define new_obj(type, name) type* name = new type;

class number // a class that contains a number starting at 0 for testing my ptr, announces when it has been reconstructed with cout
{
	public:
		number();
		int n;
		~number();
};

number::number()
{
	n = 0;
}

number::~number()
{
	std::cout<<"number removed"<<std::endl;
}

class ptr
{
	private:
		void* content;
		static std::map<void*, unsigned int> others; // used for automatic Java style cleaning up
		bool first_direct;

	public:
		ptr();
		void direct(void*); // direct the pointer to point at something
		void* follow(); // follow the pointer to the object it is pointing at
		~ptr(); // the error occurs here
};

std::map<void*, unsigned int> ptr::others = *(new std::map<void*, unsigned int>);

ptr::ptr()
{
	first_direct = true;
}

ptr::~ptr()
{
	if (!first_direct)
	{
		if (others.find(content)->second == 1)
		{
			// others[content] = 0; // uncommenting this line causes an error, is supposed to set the information in the map "others" at position "content" to be the integer 0
			delete (number*) content;
		}
	}
}

void ptr::direct(void* direction)
{
	if (!first_direct)
	{
		others[content] -= 1;
	}
	content = direction;
	others[content] += 1;
	first_direct = false;
}

void* ptr::follow()
{
	return content;
}

void main()
{
	number* n1 = new number;
	number* n2 = new number;
	{
		ptr p1;
		p1.direct(n1);
	}
	{
		ptr p2;
		p2.direct(n1);
	}
	{
		ptr p3;
		p3.direct(n2);
	}
}
Last edited on
bump
bump, I would really apreceate a hand with this.
Last edited on
I think that this is a double deletion error.

1
2
3
4
5
6
7
8
	{
		ptr p1;
		p1.direct(n1);
	}//p1 get destructed here and in it's destructor  will will delete n1.
	{
		ptr p2;
		p2.direct(n1); //Error n1 has been deleted
	}


They may be other issues as well.
Last edited on
1) Why are you using void pointers? void pointers don't play nice. On line 53, in your dtor, you cast the pointer to a number* which means the ptr class will only work with pointers to 'number's, which means you might as well just make 'content' a number* instead of a void*. That way you gain typesafety and you don't have to cast to/from void pointers all the time.

2) Line 39 is a memory leak. What you're doing there is creating a new nameless object on the heap with new, then COPYING that object to ptr::others. The nameless object is never used, and is never deleted.

You could accomplish the same thing without a memory leak by not using new:

std::map<void*, unsigned int> ptr::others; // same end result

3) what guestgulkan said
What Disch said. void* has been deprecated in C++. We now use reinterpret_casts, and dynamic_casts.

Also, just so you can make sure you know what you're doing, I'm enclosing a link to the reference page for map<>.

http://cplusplus.com/doc/tutorial/typecasting/
http://cplusplus.com/reference/stl/map/

-Albatross
I think that this is a double deletion error.

ofc, I designed it that way then made an oversimplified example where each bit occured in sequence and this made it not work :¬o Thanks, couldn't see the wood for the trees. Hopefully that will solve it. It was working entirly as it was suposed to.


which means the ptr class will only work with pointers to 'number's

I hope to make it generic later.


The idea is that the pointer deletes the target object when nothing is pointing to it, like in Java. Being a pointer it needs to be able to point to anything and members called on it need to be able to be passed to the object it is pointing to. I will do this though the follow member function.


... // same end result

ty for that. I remeber that producing a compile error. I will try again.


What Disch said. void* has been deprecated in C++. We now use reinterpret_casts, and dynamic_casts.

This is the first I have heard that void* has been offically deprecated. I want to avoid constructing my pointer with a template like this.

ptr<number> p1;

I don't know if I can achieve the same thing with reinterpret_casts or dynamic_casts instead of void*.

I wish to be able to point the pointer at an int, do some int things then a std::string and call std::string members without loss of generic functionality or complicated syntax for the user.


void* allows for a lot of stuff that doesn't work with just templates. I can get an example later of a template function that is given two parameters, an object and a bool, if the bool is true it couts the object, if false then it couts the output of the length member of the object.

It is a trivial example but seemingly impossible not to get a compilation error when calling the function with <int>(int, true) and <std::string>(std::string, false) in the same program. The compiler complains that the int doesn't have a length member, even though the code never actually calls length on the int, only the string.

I can aprecate that this is the compiler trying to force type safety, but not everyone likes type safety, in some langueges you never specify things like return types and any member can be called on anything without hastle (unless you make a mistake).

Other langueges go the other way and are much more type safe than C++, which type safety proponents I know prefer to C++. C++ is verry complicated however and many people like this, especally now with two new types of casting, which appear to have complicated rules about how they work and undoubtedly complicated circumstances in which they will and won't work.

Quick example of C++ having complicated exceptions to behaviour.

works:

1
2
3
4
5
6
7
8
9
10
11
class c2

class c1
{
  public:
    c2* a;
};

class c2
{
};


fails (spot the suble difference):

1
2
3
4
5
6
7
8
9
10
11
class c2;

class c1
{
  public:
    c2 a;
};

class c2
{
};



Sorry, way off topic, I will try the fixes probbably tomorow.
Last edited on
This is the first I have heard that void* has been offically deprecated.


I don't know how "official" it is.

I think he just meant that it's a construct that isn't necessary/recommended in C++. There are numerous problems with it.

I want to avoid constructing my pointer with a template like this.


Why?

That's the only way it will work with any given complex object. (Or you could simulate a template with a macro, but ew)

The thing is, delete calls the destructor for the object being deleted. This means that delete needs to know the type in order to call the appropriate dtor. This is partly why you can't delete a void pointer, and why you had to cast it in your previous code.
That's the only way it will work with any given complex object.

If thats the case then I will, but, I might think of something, or maybe not. I'll keep you posted.


The thing is, delete calls the destructor for the object being deleted. This means that delete needs to know the type in order to call the appropriate dtor. This is partly why you cant delete a void pointer, and why you had to cast it in your previous code.

I've thought before that it would be better if constuctors were called "construct" instead of the name of the object and the same with destructors and "destruct".

If something leaves scope is it identical to be it deleted with delete? I didn't know there was any problem with the clean up of the object at the other end of a void*, presumably it's not a problem if you cast it as you would if you wanted to access a member of the target object.
Last edited on
If thats the case then I will, but, I might think of something, or maybe not. I'll keep you posted.


Well why are you so against templates? This is like the perfect example of where they're useful.

I've thought before that it would be better if constuctors were called "construct" instead of the name of the object and the same with destructors and "destruct".


Yeah I would like something like that more too. I think D did it where it used 'this' instead of the class name, so the constructor's name was MyClass::this instead of MyClass::MyClass (iirc, anyway, I could be remembering wrong).

I'd like something like that much more. But whatever.

If something leaves scope is it identical to be it deleted with delete?


Are you talking about stuff put on the stack? (not allocated with new)

They're pretty much the same, yes. At least as far as the object is concerned. There's differences in the way memory is freed, but whatever.


I didn't know there was any problem with the clean up of the object at the other end of a void*, presumably it's not a problem if you cast it as you would if you wanted to access a member of the target object.


Clean up basically consists of 2 parts:

1) run the destructor for the object
2) free the memory

In order for the compiler to run the destructor for the object it needs to know what type it is. If all it has is a void pointer with no type information, how can it know which destructor to run?

You can get around this by casting the void pointer to a typed pointer, but then that defeats the entire point of using a void pointer (ie: it can point to any type). Because now that you cast to a specific type, your code will only work properly with that specific type.

For instance, your ptr class above. What would happen if you did this:

1
2
3
std::string* foo = new std::string("example");
ptr p;
p.direct(foo);


This would compile OK because foo can be cast to a void pointer. But when p's dtor is run, it will cast the pointer to a 'number' instead of to a string! So it calls the wrong dtor, which can have disasterous results.
The code already checked that I wasn't removing the same thing twice, fixed the problem and added another feature and it now works mostly as intended but still for the moment.



The leak on this line.

std::map<void*, unsigned int> ptr::others = *(new std::map<void*, unsigned int>);


I tried to fix it be omitting it completely as I don't get why it is required, but got the error.

error LNK2001: unresolved external symbol "private: static class std::map..."


std::map<void*, unsigned int> ptr::others = std::map<void*, unsigned int>;

error C2275: 'std::map<_Kty,_Ty>' : illegal use of this type as an expression
Topic archived. No new replies allowed.