After reading all I could find about auto_ptr, I decided to write a
little program to test my comprehension:
#include <iostream>
#include <memory>
class A {
public:
void say_hi() {
std::cout << "hi!" << std::endl;
}
void say_ih() {
std::cout << "!ih" << std::endl;
}
};
void f(std::auto_ptr<A> fs_pa) {
fs_pa->say_hi();
}
int main() {
std::auto_ptr<A> pa(new A);
pa->say_ih();
f(pa);
pa->say_ih();
}
This is what I expected would happen:
1. a new A is created, and its ownership is assumed by pa.
2. the say_ih() function is called with no problems
3. when f() is called, pa is copied into fs_pa, thereby transfering
ownership of the A object to fs_pa, and leaving pa with a null pointer
4. the A object's say_hi() function is called through fs_pa
5. fs_pa goes out of scope, and therefore runs its deconstructor,
which calls 'delete' on the chunk of memory which holds the A object
6. when we return to main(), pa now holds a null pointer, and
"pa->say_ih();" should cause a runtime error.
To my surprise, this piece of code produced no errors at either compile
time or at run time. Can anybody tell me what the problem is with my
reasoning? 23 1530
Jeff wrote: After reading all I could find about auto_ptr, I decided to write a little program to test my comprehension:
#include <iostream> #include <memory>
class A { public: void say_hi() { std::cout << "hi!" << std::endl; } void say_ih() { std::cout << "!ih" << std::endl; } };
void f(std::auto_ptr<A> fs_pa) { fs_pa->say_hi(); }
int main() { std::auto_ptr<A> pa(new A); pa->say_ih(); f(pa); pa->say_ih(); }
This is what I expected would happen: 1. a new A is created, and its ownership is assumed by pa. 2. the say_ih() function is called with no problems 3. when f() is called, pa is copied into fs_pa, thereby transfering ownership of the A object to fs_pa, and leaving pa with a null pointer 4. the A object's say_hi() function is called through fs_pa 5. fs_pa goes out of scope, and therefore runs its deconstructor, which calls 'delete' on the chunk of memory which holds the A object 6. when we return to main(), pa now holds a null pointer, and "pa->say_ih();" should cause a runtime error.
It causes undefined behavior.
To my surprise, this piece of code produced no errors at either compile time or at run time. Can anybody tell me what the problem is with my reasoning?
Undefined behavior does not necessarily involve a runtime error.
Best
Kai-Uwe Bux
Jeff wrote: After reading all I could find about auto_ptr, I decided to write a little program to test my comprehension:
#include <iostream> #include <memory>
class A { public: void say_hi() { std::cout << "hi!" << std::endl; } void say_ih() { std::cout << "!ih" << std::endl; } };
void f(std::auto_ptr<A> fs_pa) { fs_pa->say_hi(); }
int main() { std::auto_ptr<A> pa(new A); pa->say_ih(); f(pa); pa->say_ih(); }
This is what I expected would happen: 1. a new A is created, and its ownership is assumed by pa. 2. the say_ih() function is called with no problems 3. when f() is called, pa is copied into fs_pa, thereby transfering ownership of the A object to fs_pa, and leaving pa with a null pointer
No, it leaves pa with no object to own. Use it and you'll get undefined
behavior.
4. the A object's say_hi() function is called through fs_pa 5. fs_pa goes out of scope, and therefore runs its deconstructor,
"destructor" (by the way)
which calls 'delete' on the chunk of memory which holds the A object 6. when we return to main(), pa now holds a null pointer, and "pa->say_ih();" should cause a runtime error.
No, it causes undefined behavior.
To my surprise, this piece of code produced no errors at either compile time or at run time. Can anybody tell me what the problem is with my reasoning?
You associate "undefined behavior" with "crash". UB may do anything,
from crashing to nothing.
Jonathan
Jonathan Mcdougall wrote: Jeff wrote: After reading all I could find about auto_ptr, I decided to write a little program to test my comprehension:
#include <iostream> #include <memory>
class A { public: void say_hi() { std::cout << "hi!" << std::endl; } void say_ih() { std::cout << "!ih" << std::endl; } };
void f(std::auto_ptr<A> fs_pa) { fs_pa->say_hi(); }
int main() { std::auto_ptr<A> pa(new A); pa->say_ih(); f(pa); pa->say_ih(); }
This is what I expected would happen: 1. a new A is created, and its ownership is assumed by pa. 2. the say_ih() function is called with no problems 3. when f() is called, pa is copied into fs_pa, thereby transfering ownership of the A object to fs_pa, and leaving pa with a null pointer
No, it leaves pa with no object to own. Use it and you'll get undefined behavior.
4. the A object's say_hi() function is called through fs_pa 5. fs_pa goes out of scope, and therefore runs its deconstructor,
"destructor" (by the way)
which calls 'delete' on the chunk of memory which holds the A object 6. when we return to main(), pa now holds a null pointer, and "pa->say_ih();" should cause a runtime error.
No, it causes undefined behavior.
To my surprise, this piece of code produced no errors at either compile time or at run time. Can anybody tell me what the problem is with my reasoning?
You associate "undefined behavior" with "crash". UB may do anything, from crashing to nothing.
Jonathan
which compiler did you use ? If you use VS 6.0, ps is not NULL. But if
you use .net 2003 or above, you will get what you expected ( ps is NULL
).
I still don't test it but I remember that VS 6.0 does not take care of
this issue.
please use this:
class A {
public:
void say_hi() {
std::cout << "hi!" << std::endl;
}
virtual void say_ih() {
std::cout << "!ih" << std::endl;
}
};
instead of your class A.
:-)
please note that i added a *virtual* before "void say_ih()"
:-)
ddh wrote: please note that i added a *virtual* before "void say_ih()"
I noted, but I don't understand. Tell me, why do you want that function
to be virtual?
:-)
This is Usenet, not a web forum. Learn how to post correctly. See http://cfaj.freeshell.org/google/.
Jonathan
OKay,
A normal member function is just like C function, they have an address,
and the compile add "this" as the first parameter automatically. so
what ever the auto_ptr hold, you call pa->say_ih(), it just jump to the
address of "say_ih()", and push the value("this") into stack. and
"this" hasn't been refered in say_ih(), so there is no access violation
here.
But if say_ih() is virtual, to get the address of it, "this" must be
accessed to get the vtbl. and because "this" has already been released,
so the program must crash for an access violation.
sorry for my poor english. i hope i express my opinion correctly. :-)
ddh wrote: OKay,
A normal member function is just like C function, they have an address, and the compile add "this" as the first parameter automatically. so what ever the auto_ptr hold, you call pa->say_ih(), it just jump to the address of "say_ih()", and push the value("this") into stack. and "this" hasn't been refered in say_ih(), so there is no access violation here.
But if say_ih() is virtual, to get the address of it, "this" must be accessed to get the vtbl. and because "this" has already been released, so the program must crash for an access violation.
sorry for my poor english. i hope i express my opinion correctly. :-)
Several things here
1) you don't seem to understand that there are guidelines for posting
in this newsgroup. You have been ignoring what I said about etiquette
for a few posts now. Please, tell us if you just don't understand,
we'll be happy to explain it again. However, if you think your posting
style is more appropriate than what's recommended, just let us know.
2) what is written in your post has nothing to do with standard C++,
the subject of this newsgroup. Implementation do not necessarily use
vtables for virtual dispatch and undefined behavior does not
necessarily mean "access violation". Please, try to restrict your
comments to keep them on topic.
Jonathan
ddh wrote: OKay,
A normal member function is just like C function, they have an address, and the compile add "this" as the first parameter automatically. so what ever the auto_ptr hold, you call pa->say_ih(), it just jump to the address of "say_ih()", and push the value("this") into stack. and "this" hasn't been refered in say_ih(), so there is no access violation here.
But if say_ih() is virtual, to get the address of it, "this" must be accessed to get the vtbl. and because "this" has already been released, so the program must crash for an access violation.
sorry for my poor english. i hope i express my opinion correctly. :-)
Yes, when I added "virtual" it made the program do what I originally
thought it would do. So if I understand right, what actually happened
in my program above (the one without 'virtual') was that the compiler
saw 'pa', realized that it was was of type auto_ptr<A>, and so simply
found A::say_ih() -- the fact that pa no longer pointed to anything was
irrelevant, right?
Thanks very much for your help!
Jonathan Mcdougall wrote: 2) what is written in your post has nothing to do with standard C++, the subject of this newsgroup. Implementation do not necessarily use vtables for virtual dispatch and undefined behavior does not necessarily mean "access violation". Please, try to restrict your comments to keep them on topic.
Jonathan
I think I have to disagree with you here; the observation that adding
'virtual' would very likely cause the undefined behavior to manifest
itself in the form of error messages really did help me understand what
was going on here.
That having been said, I do appreciate your help -- thanks!
Jeff wrote: ddh wrote: OKay,
A normal member function is just like C function, they have an address, and the compile add "this" as the first parameter automatically. so what ever the auto_ptr hold, you call pa->say_ih(), it just jump to the address of "say_ih()", and push the value("this") into stack. and "this" hasn't been refered in say_ih(), so there is no access violation here.
But if say_ih() is virtual, to get the address of it, "this" must be accessed to get the vtbl. and because "this" has already been released, so the program must crash for an access violation.
sorry for my poor english. i hope i express my opinion correctly. :-)
Yes, when I added "virtual" it made the program do what I originally thought it would do. So if I understand right, what actually happened in my program above (the one without 'virtual') was that the compiler saw 'pa', realized that it was was of type auto_ptr<A>, and so simply found A::say_ih() -- the fact that pa no longer pointed to anything was irrelevant, right?
Dereferencing an invalid std::auto_ptr results in undefined behavior.
The standard states that "permissible undefined behavior ranges from
ignoring the situation completely with unpredictable results, to
behaving during translation or program execution in a documented manner
characteristic of the environment (with or without the issuance of a
diagnostic message), to terminating a translation or execution (with
the issuance of a diagnostic message)." (1.3.12)
What happened in your case was that the dereferencing of an invalid
std::auto_ptr was "ignored with unpredictable results". From that very
statement until the end of the program, the behavior is undefined.
However, a sensible behavior would be that as long as you don't
illegally access memory, your program won't crash. It seems, in your
case, that std::auto_ptr has an unchecked operator-> (that may be
unfortunate during development) and that, on your compiler, invoking a
member function on an invalid pointer does not crash if you don't use
the this pointer.
Understand that this is "undefined behavior". Running your program on
another machine could very well blow up your monitor.
Jonathan
Jonathan Mcdougall wrote: Understand that this is "undefined behavior". Running your program on another machine could very well blow up your monitor.
Jonathan
Lol -- I hadn't realized that learning C++ could be so hazardous to my
health!!
Jeff <jk*****@gmail.com> wrote: Jonathan Mcdougall wrote: Understand that this is "undefined behavior". Running your program on another machine could very well blow up your monitor.
Lol -- I hadn't realized that learning C++ could be so hazardous to my health!!
Just watch out for the nasal demons: http://www.catb.org/jargon/html/N/nasal-demons.html
--
Marcus Kwok
Replace 'invalid' with 'net' to reply
Jeff wrote: Yes, when I added "virtual" it made the program do what I originally thought it would do. So if I understand right, what actually happened in my program above (the one without 'virtual') was that the compiler saw 'pa', realized that it was was of type auto_ptr<A>, and so simply found A::say_ih() -- the fact that pa no longer pointed to anything was irrelevant, right?
class A
{
public:
void x() {}
}
A * a = 0;
a->x();
This is pretty much the same as calling some function...
A_x(struct Aimpl * this) {}
Calling that function with a null pointer will not pose any problems as
the function never looks at or reads 'this'...therefor neither will
a->x().
By changing x to be virtual you have introduced a step in between that
does in fact dereference the null pointer. "Aimpl" will have a table
of function pointers that needs referencing before the call...you end
up with something more like:
Aimpl *a = 0;
(*a->_vtable[1])(a);
_vtable[1] pointing to the above A_x function.
This dereferences a, which in our case is 0 or NULL.
As a purely theoretical point of view of staying strictly to the
standard none of the above is true...it is not know what when how or
why. But from a practical point of view there is no implementation
that doesn't work this way...as a C++ programmer it is good to know
these things as you *can* run into situations caused by a null _vtable
inside of a valid class on some architectures in some situations (I
just did yesturday).
Should you depend on this in your code? Definately not...you should
not depend on the definedness of undefined behavior. That doesn't mean
it isn't good to know or talk about.
"Noah Roberts" <ro**********@gmail.com> skrev i meddelandet
news:11**********************@38g2000cwa.googlegro ups.com... Jeff wrote:
Yes, when I added "virtual" it made the program do what I originally thought it would do. So if I understand right, what actually happened in my program above (the one without 'virtual') was that the compiler saw 'pa', realized that it was was of type auto_ptr<A>, and so simply found A::say_ih() -- the fact that pa no longer pointed to anything was irrelevant, right?
class A { public: void x() {} }
A * a = 0;
a->x();
This is pretty much the same as calling some function...
A_x(struct Aimpl * this) {}
Calling that function with a null pointer will not pose any problems as the function never looks at or reads 'this'...therefor neither will a->x().
Unless you happen to run on hardware, where loading an invalid pointer
into an address register causes a hardware trap.
Guess why the behaviour is undefined. :-)
Bo Persson
On Wed, 17 May 2006 00:07:22 -0700, Jeff wrote: After reading all I could find about auto_ptr, I decided to write a little program to test my comprehension:
#include <iostream> #include <memory>
class A { public: void say_hi() { std::cout << "hi!" << std::endl; } void say_ih() { std::cout << "!ih" << std::endl; } };
void f(std::auto_ptr<A> fs_pa) { fs_pa->say_hi(); }
int main() { std::auto_ptr<A> pa(new A); pa->say_ih(); f(pa); pa->say_ih(); }
This is what I expected would happen: 1. a new A is created, and its ownership is assumed by pa. 2. the say_ih() function is called with no problems 3. when f() is called, pa is copied into fs_pa, thereby transfering ownership of the A object to fs_pa, and leaving pa with a null pointer 4. the A object's say_hi() function is called through fs_pa 5.
Mod #3: Assuming a standards-compilant implementation. As I recall,
certain implementations didn't set the originating pointer to NULL after
transferring ownership...
fs_pa goes out of scope, and therefore runs its deconstructor, which calls 'delete' on the chunk of memory which holds the A object 6. when we return to main(), pa now holds a null pointer, and "pa->say_ih();" should cause a runtime error.
To my surprise, this piece of code produced no errors at either compile time or at run time. Can anybody tell me what the problem is with my reasoning?
No problems at compile time as you've done nothing syntactically wrong.
The problem is that you're assuming that Undefined Behaviour == Program
Crash. You could probably increase the chances of a program crash if your
member functions tried to access member variables of your class. However,
even that's only increasing your chances. You can't guarantee that it
will crash.
Bo Persson wrote: "Noah Roberts" <ro**********@gmail.com> skrev i meddelandet news:11**********************@38g2000cwa.googlegro ups.com... Jeff wrote:
Yes, when I added "virtual" it made the program do what I originally thought it would do. So if I understand right, what actually happened in my program above (the one without 'virtual') was that the compiler saw 'pa', realized that it was was of type auto_ptr<A>, and so simply found A::say_ih() -- the fact that pa no longer pointed to anything was irrelevant, right?
class A { public: void x() {} }
A * a = 0;
a->x();
This is pretty much the same as calling some function...
A_x(struct Aimpl * this) {}
Calling that function with a null pointer will not pose any problems as the function never looks at or reads 'this'...therefor neither will a->x().
Unless you happen to run on hardware, where loading an invalid pointer into an address register causes a hardware trap.
Why would it be loading it into an address register?
Answer: it wouldn't. A call to A_x in the case when it is not virtual
is resolved by the compiler. The result is the same as calling a
function. It wouldn't load into an address register because no address
is accessed...not the one you are thinking is anyway.
You are forgetting that the source code and the machine code do not
have to be at all equivelant. In these cases a-> doesn't actually
result in machine code that accesses a pointer...it results in a
function call that is passed a pointer.
It could be implemented so that all function calls really result in
something like:
struct A
{
void (*f)(A * this);
A() : f(&A_x) {}
};
now you have a virtual function...non-virtuals aren't implemented this
way...it's inefficient for that purpose.
Again, yes...it is undefined...good luck finding a case when this isn't
true though. It is undefined for such a theoretical implementation but
I don't know of any...so if you find one let me know.
Jeff wrote: After reading all I could find about auto_ptr, I decided to write a little program to test my comprehension:
#include <iostream> #include <memory>
class A { public: void say_hi() { std::cout << "hi!" << std::endl; } void say_ih() { std::cout << "!ih" << std::endl; } };
void f(std::auto_ptr<A> fs_pa) { fs_pa->say_hi(); }
int main() { std::auto_ptr<A> pa(new A); pa->say_ih(); f(pa); pa->say_ih(); }
This is what I expected would happen: 1. a new A is created, and its ownership is assumed by pa.
Yes.
2. the say_ih() function is called with no problems
Yes
3. when f() is called, pa is copied into fs_pa, thereby transfering ownership of the A object to fs_pa, and leaving pa with a null pointer
Yes.
4. the A object's say_hi() function is called through fs_pa
Yes.
5. fs_pa goes out of scope, and therefore runs its deconstructor, which calls 'delete' on the chunk of memory which holds the A object
Yes.
6. when we return to main(), pa now holds a null pointer, and "pa->say_ih();" should cause a runtime error.
No. It should invoke undefined behavior. That means anything can happen,
including appearing to produce no error at all.
"Noah Roberts" <ro**********@gmail.com> skrev i meddelandet
news:11**********************@j73g2000cwa.googlegr oups.com... Bo Persson wrote: "Noah Roberts" <ro**********@gmail.com> skrev i meddelandet news:11**********************@38g2000cwa.googlegro ups.com... > > Jeff wrote: > >> Yes, when I added "virtual" it made the program do what I >> originally >> thought it would do. So if I understand right, what actually >> happened >> in my program above (the one without 'virtual') was that the >> compiler >> saw 'pa', realized that it was was of type auto_ptr<A>, and so >> simply >> found A::say_ih() -- the fact that pa no longer pointed to >> anything >> was >> irrelevant, right? > > class A > { > public: > void x() {} > } > > A * a = 0; > > a->x(); > > This is pretty much the same as calling some function... > > A_x(struct Aimpl * this) {} > > Calling that function with a null pointer will not pose any > problems > as > the function never looks at or reads 'this'...therefor neither > will > a->x(). Unless you happen to run on hardware, where loading an invalid pointer into an address register causes a hardware trap.
Why would it be loading it into an address register?
Because the calling convention specifies that the 'this' pointer is
passed in an address register. :-) Answer: it wouldn't. A call to A_x in the case when it is not virtual is resolved by the compiler. The result is the same as calling a function. It wouldn't load into an address register because no address is accessed...not the one you are thinking is anyway.
So the standard specifies how address registers are handles in
function calls?
No, it doesn't. It is unspecified! You are forgetting that the source code and the machine code do not have to be at all equivelant. In these cases a-> doesn't actually result in machine code that accesses a pointer...it results in a function call that is passed a pointer.
And how is the pointer passed to the function? It could be implemented so that all function calls really result in something like:
It *could*, but the standard doesn't specify this, so it is undefined!
Bo Persson
Bo Persson wrote: Because the calling convention specifies that the 'this' pointer is passed in an address register. :-)
Cite please.
Here is a second example that crashes as expected:
#include <iostream>
#include <memory>
class A {
public:
A(double value) {
dynAllocMem = new double;
*dynAllocMem = value;
}
~A() {
delete dynAllocMem;
}
void say_hi() {
std::cout << "hi!" << *dynAllocMem << std::endl;
}
void say_ih() {
std::cout << "!ih" << *dynAllocMem << std::endl;
}
private:
double *dynAllocMem;
};
void f(std::auto_ptr<A> fs_pa) {
fs_pa->say_hi();
}
int main() {
std::auto_ptr<A> pa(new A(1.0));
pa->say_ih();
std::cout << &A::say_hi << std::endl;
f(pa);
std::cout << &A::say_hi << std::endl;
std::auto_ptr<A> pa2(new A(2.0));
pa->say_ih();
}
themachine:test $ ./a.out
!ih1
1
hi!1
1
Bus error
Hopefully that demonstrates the issue using a dynamically allocated
member variable as opposed to the vtable.
liam_herron wrote: Here is a second example that crashes as expected:
Hopefully that demonstrates the issue using a dynamically allocated member variable as opposed to the vtable.
Yes, anything that accesses a member variable of a that isn't static
will cause a crash in all implementations I know of due to the reasons
I mentioned. Anything else is very likely not to...for the reasons I
mentioned.
In the end this means undefined behavior does not necissarily mean a
crash all the time nor does it mean "unpredictable". All it means is
that the standard does not define what will happen, if anything. We
know what will occur in most, if not all, implementations and don't
have to pretend we don't even if the standard doesn't qualify what
happens. Sure, according to the standard you could suddenly give birth
to numerous nasal demons because you illicit undefined behavior in your
compiler...but that is a highly unlikely scenario in real practice. This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: BekTek |
last post by:
How do you think?
and why?
|
by: ma740988 |
last post by:
Part of my confusion here is certainly my ignorance of templates and
std::auto_ptr. Two topics, I've perused but need to really _delve_
into. In any event, consider the 'test' source.
#...
|
by: gg |
last post by:
I am getting the following compilation errors with the following
program. My compiler is aCC 03.27 on HP-UX11 -
#include <iostream>
using namespace std;
#include <list>
#include <memory>...
|
by: Rein Anders Apeland |
last post by:
Consider the following working code:
#include <memory>
#include <iostream>
void print_ptr( const std::auto_ptr< int > & thePtr =
std::auto_ptr< int >() )
{
|
by: Stephan Hoffmann |
last post by:
Hi,
I'm new to std::auto_ptr<> and wanted to use it with
a base class and several derived classes.
I'm using gcc 3.3.5 and get a compile error I don't know
how to resolve when compiling the...
|
by: dragoncoder |
last post by:
Hi all,
I am trying to understanding std::auto_ptr<Tclass implementation from
"The C++ standard library" by Nicolai Josuttis. He gives a sample
implementation of auto_ptr class template in...
|
by: dragoncoder |
last post by:
Hi all,
I am trying to understand the auto_ptr_ref role in the implementation
of auto_ptr<>. I read the information on net but still not 100% sure of
it. My plan is as follows.
1. To see the...
|
by: Pep |
last post by:
I have a method in a class like this
class myClass
{
... ctros, dtor, etc ...
string myClassMethod()
{
string myString = "";
|
by: Andre Siqueira |
last post by:
Hello all,
I have a member function like thist:
Query(const std::string & id, std::auto_ptr<Modifiermodif =
std::auto_ptr<Modifier>())
when a try to instantiate a Query like
...
|
by: Charles Arthur |
last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
|
by: ryjfgjl |
last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
|
by: emmanuelkatto |
last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud.
Please let me know.
Thanks!
Emmanuel
|
by: BarryA |
last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
|
by: marktang |
last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers,...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
|
by: Hystou |
last post by:
Overview:
Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
|
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
| |