PDA

View Full Version : Template class



steg90
12th June 2007, 10:54
Hi,

I have just wrote the following code ( only showing part of class ) :



template<class T> class DAPointer {
T* m_pPtr;
public:
inline DAPointer( T* p=0 ) : m_pPtr(p){}
inline ~DAPointer() { m_pPtr = NULL; }
// etc....
};



What it is meant to do is just set a pointer to NULL after I've deleted it.

For example :



DAPointer<TestObject>obj;
obj = new TestObject;
delete obj; // <---------------- error : cannot convert from DAPointer<T> to void *


What am I missing in order to allow the delete function not to give conversion error?

Kind regards,
Steve

Michiel
12th June 2007, 11:16
The delete operator works only on an actual pointer. You are using it on an object on the stack.

That might work if you overload the delete operator. But I've never tried that before, and I don't think it could work on a non-pointer. You could make a delete() function, though.

But I don't see why you would want to give a member-variable the value NULL if the whole object is deleted anyway (which is what's happening when the destructor is called).

I think the delete() function is what you want.

steg90
12th June 2007, 11:27
Hi,

I want to give the member variable NULL, just incase the user forgot, a little like how QPointer works, so I don't have any 'dangling' pointers. Thus create an object, then when I delete it, it gets set to NULL for me by this class.

I thought the object is on the heap by having the constructor within the template class taking a pointer?

Possible need to override delete, thus :



void operator delete (void *p);




DAPointer<T>* pc = static_cast<DAPointer<T>*>(p);
free(p);
p = NULL;


I'll have a look, but thought I wouldn't need to do this...

Regards,
Steve

Methedrine
12th June 2007, 11:28
Looks very much like what QPointer does, maybe you want to take a look at the qt sources to see how the Trolls did it :-)

Edit: you were faster than me commenting that :D, also I think Alexandrescu's Loki library has such a pointer.

steg90
12th June 2007, 11:38
Hi,

I've had a look at the QPointer header class and don't see any overloaded delete function?

This is now my class :



template<class T> class DAPointer {
T* m_pPtr;
public:
inline DAPointer( T* p=0 ) : m_pPtr(p){}
inline ~DAPointer() { delete m_pPtr; m_pPtr = NULL; }
inline void operator delete (void *p)
{ DAPointer<T>* pc = static_cast<DAPointer<T>*>(p); free(p); p = NULL; };
};



So I thought now that the following would call the delete function above :



m_pView = new CanView();
delete m_pView;


As m_pView is DAPointer<CanView>m_pCanView

Unfortunately, doesn't appear my delete function is being used as getting the usual cannot convertfrom DAPointer<T> to void* error...

Anybody see anything incorrect?

Regards,
Steve

wysota
12th June 2007, 11:45
QPointer is used for storing pointers to objects you don't own, thus there is no point in calling delete on them. In your case you want something I recently called a hijacking pointer. In your case I don't see why would you ever want to call delete on such an object, it doesn't make sense. The object will get deleted when it runs out of scope (for example when the function it was created in returns) and when it happens, it'll delete the object contained within. I assume that by calling "delete" you'd like to delete the object pointed by the "pointer". But then you're left with an object that doesn't point to anything, so it's as good as deleting the "pointer" object itself.

Be warned that in currect situation this will cause a problem:

void func(){
YourPointer ptr1(new SomeObject());
YourPointer ptr2 = ptr1;
} // segfault here because "SomeObject" is deleted twice

steg90
12th June 2007, 11:50
Thanks,

Just wanted to set the pointer to NULL when out of scope, I know when I do a delete on an object that is of QPointer, that object is set to NULL for me.

Still unsure why my overloaded delete function not getting called...

Regards,
Steve

Michiel
12th June 2007, 11:57
I don't think steg90 wants to make a hijacking pointer. It is my understanding he wants normal pointers, except that a delete on them also sets them to NULL.

Of course, you can't catch a delete that happens elsewhere in the program (where other pointers point to the same object) except if the pointer class only points to special classes that will notify it. This is what QPointer does. It can only point to QObject descendants.

So the best you can do is make the pointer NULL after you call delete on that specific pointer. Which makes the class purely a convenience class.

You can't overload the delete operator to take a non-pointer. That is where the compiler error comes from. So a del() function will have to do.

Here is your convenience class:


template<class T>
class Pointer {
public:

Pointer (T* p) {
_ptr = p;
}

void del() {
delete _ptr;
_ptr = NULL;
}

// More operators. Like the dereferencing operators: * and ->

private:

T* _ptr;
};

I just read your latest post, steg90. Why would you want to give a pointer the value NULL if it falls out of scope anyway?

steg90
12th June 2007, 12:03
Hi,

So I would have to call del()? Now the user needs to remember to call that...

I thought QPointer took a new object and when I deleted that object it sets it to NULL for me, just was trying to emulate that in my own template class, but as you can see, no success...

Regards,
Steve

wysota
12th June 2007, 12:04
By the way... a somewhat smarter approach would be:

template<class T> class SmartPointer {
struct SPData {
SPData(T *obj){ m_ptr = obj; m_count = 1; }
~SPData(){ delete m_ptr; }
T* m_ptr;
uint m_count;
};
public:
SmartPointer(T *obj=0){
m_data = new SPData(obj);
}
~SmartPointer(){
deref();
}
SmartPointer(SmartPointer &other){
m_data = other.m_data;
ref();
}
SmartPointer &operator=(SmartPointer &other){
if(other.m_data==m_data)
return *this;
deref();
m_data = other.m_data;
ref();
}
SmartPointer &operator=(T *obj){
if(!m_data){
m_data = new SPData(obj);
return *this;
}
if(obj==m_data->m_ptr) return *this;
m_data->deref();
m_data = new SPData(obj);
return *this;
}
bool operator==(SmartPointer &other) const{ return m_data==other.m_data; }
T operator*(){ return m_data ? m_data->m_ptr : T(); }
const T* operator T*() const { return m_data ? m_data->m_ptr : 0; }
private:
SPData *m_data;
void deref(){
if(!m_data) return;
if((--(m_data->m_count))==0){
delete m_data;
}
}
void ref(){
m_data->m_count++;
}
int m_count;
};
Note that this code is not reentrant and was not tested thus it probably contains some errors. Furthermore the mechanism will still break down if you delete the pointer yourself somewhere. But the advantage is that all objects track the number of references to the object and the object will get deleted when nothing points to it anymore. If you want, you can also add a possibility to force the deletion by adding the following method:

void SmartPointer::forceDelete(){
if(!m_data) return;
delete m_data->m_ptr;
m_data->m_ptr = 0;
}
Adding isValid() or isNull() methods would also be a good move then.

steg90
12th June 2007, 12:34
Many thanks to all of you for your suggestions and help.

wysota - class looks cool :p

wysota
12th June 2007, 13:03
wysota - class looks cool :p
Thx :) It probably doesn't work, but still thx :)

A bit more about QPointer - the whole magic in QPointer is that it connects to the destroyed() signal emitted from every QObject when it gets deleted, so it's easy to synchronize all pointers and it's safe to use it when regular pointers point to the same object. The minimal implementation of QPointer is:


class MyPointer : public QObject {
Q_OBJECT
public:
MyPointer(QObject *object, QObject *parent=0){
m_ptr = object;
if(m_ptr)
connect(m_ptr, SIGNAL(destroyed()), SLOT(_q_destroyed()));
}
// stuff returning and operating on the pointer goes here, like:
operator T * () const{ return m_ptr; }
private slots:
void _q_destroyed(){
m_ptr =0;
}
private:
QObject *m_ptr;
}

steg90
12th June 2007, 13:53
I now see how QPointer is working :rolleyes:

I have now been using auto_ptr<T> which is pretty similar to the smart pointers you get in the boost libs.

Regards,
Steve

wysota
12th June 2007, 16:22
Ask Jacek, he'll tell you that std::auto_ptr sucks :)

fullmetalcoder
12th June 2007, 17:33
the whole magic in QPointer is that it connects to the destroyed() signal emitted from every QObject when it gets deleted
Really? And how do you think it would connect to anything? :rolleyes: QPointer does not inherit from QObject... The whole magic is, once again, what some would call a "dirty hack". It's handled somewhere in QMetaObject::addGuard() and QMetaObject::removeGuard() if I remember well (note that these functions are, of course, internals...) and proves faster (and in some cases safer) than signals/slots, especially when playing with threads...

wysota
12th June 2007, 20:49
Really? And how do you think it would connect to anything? :rolleyes: QPointer does not inherit from QObject...
It doesn't matter. The general mechanism remains the same. Signals and slots are just a manifestation of the meta-object architecture. It doesn't matter whether you call connect() or use any other method that relies on the same assumptions (read the article about dynamic signals and slots - are they really signals and really slots? does it matter?). You can perfectly well emulate QPointer with a connection to destroyed() signal, even if QPointer itself is not a QObject (it could contain one internally). There are many things in Qt that are implemented "differently" under the hood than it may seem. Designer is one of them, for example - much Qt functionality is obtained using dedicated mechanisms or dirty hacks.