Is this strange behavior?

B

Bo Yang

Following is my code:

include <iostream>

class Test{
public:
Test(){};
void print(){ std::cout << "OK" << std::endl ; };
};

int main( int argc , char ** argv ){
Test * test = new Test();
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.
But it output OK normally, is this behavior right?
 
M

Markus Moll

Hi

Bo said:
int main( int argc , char ** argv ){
Test * test = new Test();
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.
But it output OK normally, is this behavior right?

Absolutely, you call print on a copy of test that is still existent.

Markus
 
R

Rud1ger Sch1erz

Bo Yang said:
int main( int argc , char ** argv ){
Test * test = new Test();
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.
No.

But it output OK normally, is this behavior right?

Yes. r is a local variable (automatic), which is still in scope at
that line (actually in this block). It's not a copy of the pointer of
object test, but a copy of the value of test into its own memory.

Cheers,
Rudiger
 
M

Mike Wahler

Bo Yang said:
Following is my code:

include <iostream>

class Test{
public:
Test(){};
void print(){ std::cout << "OK" << std::endl ; };
};

int main( int argc , char ** argv ){
Test * test = new Test();
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.

Why do you think so?

But it output OK normally, is this behavior right?

Yes, it is.

-Mike
 
T

terminator

Bo said:
Following is my code:

include <iostream>

class Test{
public:
Test(){};
void print(){ std::cout << "OK" << std::endl ; };
};

int main( int argc , char ** argv ){
Test * test = new Test();
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.
But it output OK normally, is this behavior right?
It IS correct .the compiler tries to make a copy constructor for every
class if you dont provide one.You did produce a default constructor but
u did not provide a copy constructor.

cheers
FM
 
S

Salt_Peter

Bo said:
Following is my code:

include <iostream>

class Test{
public:
Test(){};

loose the extra semicolons - Test() { } is not a declaration only
void print(){ std::cout << "OK" << std::endl ; };

void print() const { std::cout << "OK" << std::endl ; }
};

int main( int argc , char ** argv ){
Test * test = new Test();

Test* test = new Test;
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.

No it won't. The following statement:
Test r = *test ;
invokes a copy constructor.
The variable r is therefore allocated on the stack and survives until
the end of main's scope.
But it output OK normally, is this behavior right?

Yes, r is an independant variable.

Also, when you are testing for undefined behaviour, stick a dummy
variable in the class and test by attempting to access the dummy.

example:

#include <iostream>

class Test {
int n;
public:
Test() : n( 0 ) { }
int get() const {
return n;
}
void print() const {
std::cout << "OK\n";
}
};

int main() {
Test * p_test = 0; // pointer to a black hole
p_test->print(); // passes
// p_test->get(); // fails
}
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Also, when you are testing for undefined behaviour, stick a dummy
variable in the class and test by attempting to access the dummy.

example:

#include <iostream>

class Test {
int n;
public:
Test() : n( 0 ) { }
int get() const {
return n;
}
void print() const {
std::cout << "OK\n";
}
};

int main() {
Test * p_test = 0; // pointer to a black hole
p_test->print(); // passes
// p_test->get(); // fails
}

A question: I can understand why it works even if p_test == 0, but
should it work (is it legal)?
 
P

peter koch

Salt_Peter skrev:
Bo Yang wrote: [snip]
int main() {
Test * p_test = 0; // pointer to a black hole
p_test->print(); // passes
Well... it passes in some sense of the word, surely. But it is
undefined behaviour and the statement there is not required to do
anything sensible. It might "work", it might abort the program and it
might send a rude email to your boss.


/Peter
 
P

Puppet_Sock

Salt_Peter wrote:
[snip]
Also, when you are testing for undefined behaviour, stick a dummy
variable in the class and test by attempting to access the dummy.

example:

#include <iostream>

class Test {
int n;
public:
Test() : n( 0 ) { }
int get() const {
return n;
}
void print() const {
std::cout << "OK\n";
}
};

int main() {
Test * p_test = 0; // pointer to a black hole
p_test->print(); // passes

What does the standard say should happen when you do this?
Personally, I would make a serious effort never to do -> on a
null pointer, even if it's to a function with no access to data.
// p_test->get(); // fails
}

Socks
 
S

Salt_Peter

Erik said:
A question: I can understand why it works even if p_test == 0, but
should it work (is it legal)?

Its *not* legal. Even if it appears to work. Thats my point.
The code you presented earlier does not invoke undefined behaviour. To
prove it, use get() as described in above.
A runtime error will not occur unless you attempt to access the invalid
object. So you can use something like get() to test for UB while
print() will erroneously fail to generate any error.
 
S

Salt_Peter

Puppet_Sock said:
Salt_Peter wrote:
[snip]
Also, when you are testing for undefined behaviour, stick a dummy
variable in the class and test by attempting to access the dummy.

example:

#include <iostream>

class Test {
int n;
public:
Test() : n( 0 ) { }
int get() const {
return n;
}
void print() const {
std::cout << "OK\n";
}
};

int main() {
Test * p_test = 0; // pointer to a black hole
p_test->print(); // passes

What does the standard say should happen when you do this?
Personally, I would make a serious effort never to do -> on a
null pointer, even if it's to a function with no access to data.
// p_test->get(); // fails
}

Socks

Personally, i wouldn't be using a pointer at all. Let alone a null
pointer. Unfortunately, the real world doesn't neccessarily work that
way. The point i'm trying to make is that a test with get(), as opposed
to print() will correctly diagnose the error in the code and prove UB.
And yes, the standard considers the above as UB outright, which basicly
means anything can happen - including success or a reformat of the HD.
 
B

Bo Yang

Bo Yang :
Following is my code:

include <iostream>

class Test{
public:
Test(){};
void print(){ std::cout << "OK" << std::endl ; };
};

int main( int argc , char ** argv ){
Test * test = new Test();
Test r = *test ;

delete test ;
r.print(); //this line , this line
}

I think the comment line will produce an error.
But it output OK normally, is this behavior right?

I am sorry, I have paste the wrong code here.
the variable r is a reference in my brain, I mean
the following code:

#include <iostream>

class Test{
public:
Test(){};
void print(){ std::cout << "OK" << std::endl ; };
};

int main( int argc , char ** argv ){
Test * test = new Test();
Test& r = *test ;

delete test ;
r.print(); //this line , this line
}


You know, the reference r has reference to nothing
when the test object to be deleted. So, why the program
run normally whiout a crash!
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Bo Yang :

I am sorry, I have paste the wrong code here.
the variable r is a reference in my brain, I mean
the following code:

#include <iostream>

class Test{
public:
Test(){};
void print(){ std::cout << "OK" << std::endl ; };
};

int main( int argc , char ** argv ){
Test * test = new Test();
Test& r = *test ;

delete test ;
r.print(); //this line , this line
}


You know, the reference r has reference to nothing
when the test object to be deleted. So, why the program
run normally whiout a crash!

Because you don't try to access any of it's members (methods are not
members of the instance, but of the class). As Salt_Peter pointed out in
another post, if you try to access a member it will most probably crash.
Change Test to this and try again (don't forget to #include <string>):

class Test {
private:
std::string text;
public:
Test() : text("OK") {}
void print() { std::cout << text << std::endl; }
};

The reason is that when you declare a class like this what really
happens is that the print-method get an additional parameter added by
the compiler. So print acctually looks like this:

void print(Test* t) { ... }

And when you call print on an instance of Test like this:

Test t;
t.print();

It's converted to this:

Test t;
print(&t);

Now, if print looks like above:

void print() { std::cout << text << std::endl; }

This is then converted to

void print(Test* t) { std::cout << t->text << std::endl; }

As you can see, you can thus call any method on any pointer to Test as
long as the method does not actually try to access any of the class's
members, but when you do you have a problem. Of course you could write
code that try to access a member even if it's not needed just to avoid
things like those, but there's really no benefit (it would only slow
things down).

Disclaimer: I don't claim that this is the way things work, but it's a
good way of thinking about it. Different compilers might do things
differently.
 
G

Gavin Deane

Disclaimer: I don't claim that this is the way things work, but it's a
good way of thinking about it. Different compilers might do things
differently.

For the benefit of the OP, I would strengthen your disclaimer. Because
the behaviour of the code is formally undefined, finding an consistent
explanation (such as was presented here) is not sufficient for you to
rely on the behaviour. Just as different compilers might do things
differently, different versions of the same compiler, different builds
(e.g. debug vs release) or even just changing some compiler options and
switches could perfectly legitimately change the behaviour. And even if
you think you have an explanation of the behaviour, you can't be sure
no other bad things are going on as well.

So in short, do not write code like this ever.

Gavin Deane
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top