Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A Sample auto_ptr implementation

160 views
Skip to first unread message

Ankur Arora

unread,
Oct 13, 2008, 7:58:59 AM10/13/08
to
Hi All,

I'm building a sample application that uses a custom auto_ptr
implementation.
The program crashes with the following output:-

Output (Debug Assertion failed)
----------


release called
copy constructor
aap: ptr2= 10
aap: ptr3= 20
aap: ptr3= 10
aap: exiting app
Deleting pointee...10
Deleting pointee...-572662307 (...problem ?)

Code
--------

#include<iostream>

using namespace std;

// MyAutoPtr Interface
template <class T>
class MyAutoPtr
{
public:

explicit MyAutoPtr(T* ptr=0);

~MyAutoPtr();

template <class U>
MyAutoPtr(MyAutoPtr<U> *rhs);

//copy constructor member template, initialize this member pointer
with any other compatible auto_ptr
template <class U>
MyAutoPtr(MyAutoPtr<U>& rhs);

//assignment operator
template <class U>
MyAutoPtr<T>& operator=(MyAutoPtr<U>& rhs);

//relinquish ownership
T* release();

T* get() const;

//reset the auto_ptr, delete pointee, assume ownership of p
void reset(T* ptr=0);

T& operator*() const;
T* operator->() const;


private:

T* pointee;
//template <class U>
//friend class MyAutoPtr<U>;
};

//Implementation

template<typename T>
inline MyAutoPtr<T>::MyAutoPtr(T* ptr):pointee(ptr)
{ }

template <class T>
inline MyAutoPtr<T>::~MyAutoPtr()
{
if(pointee)
{
cout<<"\n\t Deleting pointee..."<<*pointee;
delete pointee;
}
}

template <class T>
template <class U>
inline MyAutoPtr<T>::MyAutoPtr(MyAutoPtr<U>& rhs) :
pointee(rhs.release())
{
cout<<"\n\t copy constructor";
}

template<class T>
template<class U>
MyAutoPtr<T>& MyAutoPtr<T>::operator=(MyAutoPtr<U>& rhs)
{
cout<<"\n\t Inside operator=";
if(this!=&rhs)
{
cout<<"\n\t Inside operator=, calling reset";
reset(rhs.release());
}
return *this;
}

template <class T>
T* MyAutoPtr<T>::get() const
{
return pointee;
}

template <class T>
inline T& MyAutoPtr<T>::operator *() const
{
return *pointee;
}

template<class T>
inline T* MyAutoPtr<T>::operator->() const
{ return pointee; }

template<class T>
T* MyAutoPtr<T>::release()
{
cout<<"\n\t release called";
T* oldp=pointee;
pointee=0;

return oldp;
}

template<class T>
void MyAutoPtr<T>::reset(T* p)
{
cout<<"\n\t reset called";
if(pointee!=p)
{
delete pointee;
//pointee=0;
}

pointee=p;
}

void main()
{
MyAutoPtr<int> intp(new int(10));
MyAutoPtr<int> intp2(intp);
MyAutoPtr<int> intp3(new int(20));

//cout<<"ptr1= "<<*intp;
cout<<"\n\t aap: ptr2= "<<*intp2;
cout<<"\n\t aap: ptr3= "<<*intp3;

intp3 = intp2; //
==============================> 1

cout<<"\n\t aap: ptr3= "<<*intp3;

cout<<"\n\t aap: exiting app";
}

It seems, on debugging, that class's custom operator= is somehow not
been called at 1 and hence a problem while destructing. (since delete
is being called twice on the same pointer)
---------------------------------------------------------------------------------------------------------

Q1. Any problems with the above code ?
Q2. What is causing operator= not to be called and how to fix it ?
Q3. Following statements (in the class interface) cause compiler
errors, if not commented out

private:
T* pointee;
//template <class U>
//friend class MyAutoPtr<U>;

MSDN defines this error as:-

Compiler Error C3772
Error Message
"name" : invalid friend template declaration

It is invalid to declare a friend of a class template specialization.
You cannot declare an explicit or partial specialization of a class
template and in the same statement declare a friend of that
specialization. The name placeholder identifies the invalid
declaration.
To correct this error
-Do not declare a friend of a class template specialization.
-If appropriate for your application, declare a friend of the class
template, or declare a friend of a particular partial or explicit
specialization.

Why such error, if this is not a specialization? or is it a
specialization somehow?
---------------------------------------------------------------------------------------------------------

Any help will be highly appreciated!

Thanks!

Yakov Gerlovin

unread,
Oct 13, 2008, 8:36:38 AM10/13/08
to
Try to define assignment operator without template U parameter:

//assignment operator
MyAutoPtr<T>& operator=(MyAutoPtr<T>& rhs);

Why do you need that 'U' anyway, release should return the pointer to
be passed to reset, so T and U should be equal.

Kai-Uwe Bux

unread,
Oct 13, 2008, 8:53:52 AM10/13/08
to
Ankur Arora wrote:

> Hi All,
>
> I'm building a sample application that uses a custom auto_ptr
> implementation.
> The program crashes with the following output:-
>
> Output (Debug Assertion failed)
> ----------
>
>
> release called
> copy constructor
> aap: ptr2= 10
> aap: ptr3= 20
> aap: ptr3= 10
> aap: exiting app
> Deleting pointee...10
> Deleting pointee...-572662307 (...problem ?)
>
> Code
> --------

Just a data point: your code works for me with output

release called
copy constructor
aap: ptr2= 10
aap: ptr3= 20

Inside operator=
Inside operator=, calling reset
release called
reset called


aap: ptr3= 10
aap: exiting app
Deleting pointee...10

So I just have nits:


> #include<iostream>
>
> using namespace std;

It's a bad idea to use using namespace std in a header.

[snip]

> template <class T>
> inline MyAutoPtr<T>::~MyAutoPtr()
> {
> if(pointee)
> {
> cout<<"\n\t Deleting pointee..."<<*pointee;
> delete pointee;
> }
> }

delete will perform its own check for 0. It is required to be a null-op in
that case.


[snip]


> template<class T>
> template<class U>
> MyAutoPtr<T>& MyAutoPtr<T>::operator=(MyAutoPtr<U>& rhs)
> {
> cout<<"\n\t Inside operator=";
> if(this!=&rhs)

This if-clause will not compile when T and U are different.

You could use

if ( this->pointee == rhs.pointee )

instead (I think).

> {
> cout<<"\n\t Inside operator=, calling reset";
> reset(rhs.release());
> }
> return *this;
> }

[snip]
> void main()
[snip]

Main returns int. I get an error with g++ at this point.


> It seems, on debugging, that class's custom operator= is somehow not
> been called at 1 and hence a problem while destructing. (since delete
> is being called twice on the same pointer)
> ---------------------------------------------------------------------------------------------------------
>
> Q1. Any problems with the above code ?

It works with g++.

> Q2. What is causing operator= not to be called and how to fix it ?

A bug in your compiler?


[snip]


Best

Kai-Uwe Bux

Kai-Uwe Bux

unread,
Oct 13, 2008, 8:58:39 AM10/13/08
to
Yakov Gerlovin wrote:

He needs it to support polymorphism. When D is derived from B, MyAutoPtr<B>
should be constructible and assignable from MyAutoPtr<D>.

But the OP could define copy-constructor and assignment operator from T
separately. For the copy-constructor, I recall that this is needed. I am
not sure if it is needed for the assignment operator.


Best

Kai-Uwe Bux

anon

unread,
Oct 13, 2008, 9:17:35 AM10/13/08
to
> [snip]
>> template <class T>
>> inline MyAutoPtr<T>::~MyAutoPtr()
>> {
>> if(pointee)
>> {
>> cout<<"\n\t Deleting pointee..."<<*pointee;
>> delete pointee;
>> }
>> }
>
> delete will perform its own check for 0. It is required to be a null-op in
> that case.
>

Check for 0 is required for printing the debug message. Other then that,
the check should be removed

Ankur Arora

unread,
Oct 13, 2008, 9:59:26 AM10/13/08
to

Hi Yakov,

Thanks for the reply!

Yes indeed when I remove the member template function i.e. change the
function as you suggested, it works.
But I don't know whats the problem with the first version.

I should also mention that I'm using visual studio 2008 express
edition.
This be a compiler issue, u reckon?

Thanks!

Ankur Arora

unread,
Oct 13, 2008, 10:10:23 AM10/13/08
to
Thanks for the reply Kai!
Please see the comments below.

Agreed!

> [snip]
>
> > template <class T>
> > inline MyAutoPtr<T>::~MyAutoPtr()
> > {
> >   if(pointee)
> >   {
> >     cout<<"\n\t Deleting pointee..."<<*pointee;
> >     delete pointee;
> >    }
> > }
>
> delete will perform its own check for 0. It is required to be a null-op in
> that case.

I did this since removing this check caused the program to crash, even
though I had made pointee equals to 0 in release(), which was used in
the copy constructor.
I'm using visual studio 2008 express edition. You reckon this is a
complier issue?

>
> [snip]
>
> > template<class T>
> > template<class U>
> > MyAutoPtr<T>& MyAutoPtr<T>::operator=(MyAutoPtr<U>& rhs)
> > {
> >   cout<<"\n\t Inside operator=";
> >   if(this!=&rhs)
>
> This if-clause will not compile when T and U are different.
>
> You could use
>
>   if ( this->pointee == rhs.pointee )
>
> instead (I think).
>

I saw this example in "More Effective C++" by scott meyers. This was
how its used in that book. Here is a code.

//Interface
template<class T>
class auto_ptr {
public:
explicit auto_ptr(T *p = 0); // see Item 5 for a
// description of
"explicit"
template<class U> // copy constructor member
auto_ptr(auto_ptr<U>& rhs); // template (see Item 28):
// initialize a new
auto_ptr
// with any compatible
// auto_ptr
~auto_ptr();
template<class U> // assignment operator
auto_ptr<T>& // member template (see
operator=(auto_ptr<U>& rhs); // Item 28): assign from
any
// compatible auto_ptr
T& operator*() const; // see Item 28
T* operator->() const; // see Item 28
T* get() const; // return value of current
// dumb pointer
T* release(); // relinquish ownership of
// current dumb pointer
and
// return its value
void reset(T *p = 0); // delete owned pointer;
// assume ownership of p
private:
T *pointee;
template<class U> // make all auto_ptr
classes
friend class auto_ptr<U>; // friends of one another
};

//Implementation
template<class T>
inline auto_ptr<T>::auto_ptr(T *p)
: pointee(p)
{}
template<class T>
inline auto_ptr<T>::auto_ptr(auto_ptr<U>& rhs)
: pointee(rhs.release())
{}
template<class T>
inline auto_ptr<T>::~auto_ptr()
{ delete pointee; }


template<class T>
template<class U>

inline auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<U>& rhs)
{
if (this != &rhs) reset(rhs.release());


return *this;
}
template<class T>

inline T& auto_ptr<T>::operator*() const


{ return *pointee; }
template<class T>

inline T* auto_ptr<T>::operator->() const


{ return pointee; }
template<class T>

inline T* auto_ptr<T>::get() const


{ return pointee; }
template<class T>

inline T* auto_ptr<T>::release()
{
T *oldPointee = pointee;
pointee = 0;
return oldPointee;
}
template<class T>
inline void auto_ptr<T>::reset(T *p)
{
if (pointee != p) {
delete pointee;
pointee = p;
}
}

Anything wrong with the above ? (would be surprised if there is, as it
came from scott meyers)

> > Q1. Any problems with the above code ?
>
> It works with g++.
>

Again, looks like an issue with visual studio 2008 express.

Barry

unread,
Oct 13, 2008, 11:06:57 AM10/13/08
to

There's a reversion for auto_ptr (IIRC, a article is proviede by Mayer
on
this), adding auto_ptr_ref for conversion, which supports rvalue
initialization for auto_ptr. which is done transparently.

void fun(auto_ptr<T> ptr);
fun(auto_ptr(new T)); // rvalue -> auto_ptr_ref -> auto_ptr

>
> > It works with g++.
>
> Again, looks like an issue with visual studio 2008 express.

there are two problem with visual C++ 2005 in this issue:
1. as extension, "explicit" does NOT work as standard says.
needs /Za switch to turn it off.

2. auto_ptr_ref use void* rather T*

point 2 is fixed by VS 2008(none expression version,
but I expression version should ship the same lib)

>
> > > Q2. What is causing operator= not to be called and how to fix it ?
>
> > A bug in your compiler?
>
> > [snip]

--
Best Regards
Barry

Ankur Arora

unread,
Oct 15, 2008, 3:17:15 AM10/15/08
to

Thanks Barry.

Hendrik Schober

unread,
Oct 15, 2008, 11:22:20 AM10/15/08
to
Barry wrote:
> [...]

> There's a reversion for auto_ptr (IIRC, a article is proviede by Mayer
> on
> this), adding auto_ptr_ref for conversion, which supports rvalue
> initialization for auto_ptr. which is done transparently.

The guy's name is Scott Meyers and the article is here:
http://www.aristeia.com/BookErrata/auto_ptr-update.html

> [...]


> there are two problem with visual C++ 2005 in this issue:
> 1. as extension, "explicit" does NOT work as standard says.
> needs /Za switch to turn it off.

Do you have a repro for this? I'd be very interested.

> [...]
> Barry

Schobi

Barry

unread,
Oct 16, 2008, 8:52:57 PM10/16/08
to

#include <memory>

int main()
{
std::auto_ptr<int> p1 = new int(10); // this shouldn't work
std::auto_ptr<int> p2(new int(10)); // should be like this!
}

=================
d:\>cl /EHsc test.cpp

Compile successfully!
=================

d:\>cl /EHsc /Za test.cpp
cl /EHsc /Za test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42
for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.

test.cpp
test.cpp(5) : error C2440: 'initializing' : cannot convert from 'int
*' to 'std::auto_ptr<_Ty>'
with
[
_Ty=int
]
Constructor for class 'std::auto_ptr<_Ty>' is declared
'explicit'
with
[
_Ty=int
]

===========================

--
Best Regards
Barry

Hendrik Schober

unread,
Oct 17, 2008, 6:16:27 AM10/17/08
to
Barry wrote:
> On Oct 15, 11:22 pm, Hendrik Schober <spamt...@gmx.de> wrote:
>> Barry wrote:
>>> [...]
>>> There's a reversion for auto_ptr (IIRC, a article is proviede by Mayer
>>> on
>>> this), adding auto_ptr_ref for conversion, which supports rvalue
>>> initialization for auto_ptr. which is done transparently.
>> The guy's name is Scott Meyers and the article is here:
>> http://www.aristeia.com/BookErrata/auto_ptr-update.html
>>
>>> [...]
>>> there are two problem with visual C++ 2005 in this issue:
>>> 1. as extension, "explicit" does NOT work as standard says.
>>> needs /Za switch to turn it off.
>> Do you have a repro for this? I'd be very interested.
>>
>
> #include <memory>
>
> int main()
> {
> std::auto_ptr<int> p1 = new int(10); // this shouldn't work
> std::auto_ptr<int> p2(new int(10)); // should be like this!
> }
> [...]

Thanks.
It doesn't seem to be a compielr bug, though, as a simple
test with my own class makes the first line fail.

Schobi

Barry

unread,
Oct 18, 2008, 11:59:47 PM10/18/08
to

Maybe some Serive Package fix this.
I tried the code on Dinkumware
http://www.dinkumware.com/exam/default.aspx
it fails too.

--
Best Regards
Barry


James Kanze

unread,
Oct 19, 2008, 12:22:14 PM10/19/08
to

If I understand the context correctly, it is. A template
operator= will never stop the compiler from generating its
default copy operator=.

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

James Kanze

unread,
Oct 19, 2008, 12:40:25 PM10/19/08
to

> Code
> --------

> #include<iostream>

> using namespace std;

>                 ~MyAutoPtr();

> //this member pointer
> //with any other compatible auto_ptr


>                 template <class U>
>                 MyAutoPtr(MyAutoPtr<U>& rhs);

This is NOT a copy constructor, according to the standard, and
it will not prevent the compiler from generating its default
copy constructor.

>                 //assignment operator
>                 template <class U>
>                 MyAutoPtr<T>& operator=(MyAutoPtr<U>& rhs);

And this is NOT a copy assignment operator, and will not prevent
the compiler from generating its own.

Note that in the two cases above, the compiler will generate its
own versions of the functions, but they will have an argument
type MyAutoPtr<T> const&; not non-const. Which means that in
some cases, when copying, the template version you provide will
be called, rather than the official copy constructor or copy
assignment operator. (I know it sounds wierd, but it's really
rather logical: overload resolution is applied, regardless of
whether the context involves "copying" or not. Template
functions are never considered "copy" operators, but if overload
resolution chooses one, that's what gets called.)

>                 //relinquish ownership
>                 T* release();

>                 T* get() const;

>                 //reset the auto_ptr, delete pointee, assume ownership of p
>                 void reset(T* ptr=0);

>                 T& operator*() const;
>                 T* operator->() const;

>         private:
>                 T* pointee;
>                 //template <class U>
>                 //friend class MyAutoPtr<U>;
> };

> //Implementation

[... just does the obvious things]

> void main()

Shouldn't compile:-).

> {
>         MyAutoPtr<int> intp(new int(10));
>         MyAutoPtr<int> intp2(intp);
>         MyAutoPtr<int> intp3(new int(20));

Now comes the fun part. Note that all of your instances are for
the same type. So the compiler generated functions will come
into consideration.

Note too that when intp and intp3 go out of scope, they're going
to use a non-array delete on memory that was allocated with an
array new. Undefined behavior.

>         //cout<<"ptr1= "<<*intp;
>         cout<<"\n\t aap: ptr2= "<<*intp2;
>         cout<<"\n\t aap: ptr3= "<<*intp3;

>         intp3 = intp2;      // ==============================>  1
>
>         cout<<"\n\t aap: ptr3= "<<*intp3;
>         cout<<"\n\t aap: exiting app";
> }

> It seems, on debugging, that class's custom operator= is
> somehow not been called at 1 and hence a problem while
> destructing.

This would be a compiler error, but a subtle one. Since intp2
is a non-const lvalue, overload resolution should choose your
template'd operator= over the compiler generated one (which is
none the less present). Given that the context is copy, it
looks like the compiler is using copy assignment operator,
without doing full overload resolution.

> (since delete
> is being called twice on the same pointer)
> --------------------------------------------

> Q1. Any problems with the above code ?

IMHO, yes. The fact that you've let the compiler generate its
default versions will lead to runtime errors which may be
difficult to check. (Basically, you can assign a const or a
temporary MyAutoPtr, with the wrong semantics.) At the very
least, you should declare and define the copy constructor and
the assignment operator you want: non-template functions taking
a MyAutoPtr<T>& (and not the MyAutoPtr<T> const& that the
compiler will generate here).

> Q2. What is causing operator= not to be called and how to fix
> it ?

It looks like a compiler error, but I suspect that my
recommendations concerning Q1 will also fix it.

Hendrik Schober

unread,
Oct 20, 2008, 2:26:37 PM10/20/08
to
Barry wrote:
> On Oct 17, 6:16 pm, Hendrik Schober <spamt...@gmx.de> wrote:
>> Barry wrote:
>>> On Oct 15, 11:22 pm, Hendrik Schober <spamt...@gmx.de> wrote:
>>>> Barry wrote:
> [...]
>>> #include <memory>
>>> int main()
>>> {
>>> std::auto_ptr<int> p1 = new int(10); // this shouldn't work
>>> std::auto_ptr<int> p2(new int(10)); // should be like this!
>>> }
>>> [...]
>> It doesn't seem to be a compielr bug, though, as a simple
>> test with my own class makes the first line fail.
>
> Maybe some Serive Package fix this.
> I tried the code on Dinkumware
> http://www.dinkumware.com/exam/default.aspx
> it fails too.

Um, I think I've been unclear. The first line compiles with
'std::auto_ptr<>' but this fails:

class test {
public:
explicit test(void*) {}
};

int main()
{
´ test t1 = new int; // fails
test t2(new int); // compiles
return 0;
}


So it's probably not a problem of the compiler, but of the
std lib implementation that came with it.

> Best Regards
> Barry

Schobi

P.S.: BTW, never put pointers to arrays into 'std::auto_ptr<>'.

Barry

unread,
Oct 21, 2008, 10:09:04 AM10/21/08
to

yes, it only comes specially with auto_ptr

>
> > Best Regards
> > Barry
>
>   Schobi
>
>   P.S.: BTW, never put pointers to arrays into 'std::auto_ptr<>'.

I think you were confused the syntax of "new int(10)" with "new
int[10]"
it mean "new an integer with initial value 10"
--
Best Regards
Barry

Hendrik Schober

unread,
Oct 21, 2008, 11:34:52 AM10/21/08
to
Barry wrote:
> [...]

>> P.S.: BTW, never put pointers to arrays into 'std::auto_ptr<>'.
>
> I think you were confused the syntax of "new int(10)" with "new
> int[10]"
> it mean "new an integer with initial value 10"

Yes, I was. Sorry for that brainfart.

> Barry

Schobi

0 new messages