Is this strange behavior?

Discussion in 'C++' started by Bo Yang, Nov 29, 2006.

  1. Bo Yang

    Bo Yang Guest

    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?
     
    Bo Yang, Nov 29, 2006
    #1
    1. Advertising

  2. Bo Yang

    Markus Moll Guest

    Hi

    Bo Yang wrote:

    > 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
     
    Markus Moll, Nov 29, 2006
    #2
    1. Advertising

  3. Bo Yang <> writes:

    > 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
     
    Rud1ger Sch1erz, Nov 29, 2006
    #3
  4. Bo Yang

    Mike Wahler Guest

    "Bo Yang" <> wrote in message
    news:ekk9rf$58n$99.com...
    > 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
     
    Mike Wahler, Nov 29, 2006
    #4
  5. Bo Yang

    terminator Guest

    Bo Yang wrote:
    > 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
     
    terminator, Nov 29, 2006
    #5
  6. Bo Yang

    Salt_Peter Guest

    Bo Yang wrote:
    > 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
    }
     
    Salt_Peter, Nov 29, 2006
    #6
  7. On 2006-11-29 20:25, Salt_Peter wrote:
    > 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)?


    --
    Erik Wikström
     
    =?ISO-8859-1?Q?Erik_Wikstr=F6m?=, Nov 29, 2006
    #7
  8. Bo Yang

    peter koch Guest

    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_test->get(); // fails
    > }
     
    peter koch, Nov 29, 2006
    #8
  9. Bo Yang

    Puppet_Sock Guest

    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
     
    Puppet_Sock, Nov 29, 2006
    #9
  10. Bo Yang

    Salt_Peter Guest

    Erik Wikström wrote:
    > On 2006-11-29 20:25, Salt_Peter wrote:
    > > 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)?
    >


    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.
     
    Salt_Peter, Nov 29, 2006
    #10
  11. Bo Yang

    Salt_Peter Guest

    Puppet_Sock wrote:
    > 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.
     
    Salt_Peter, Nov 29, 2006
    #11
  12. Bo Yang

    Ron Natalie Guest

    Markus Moll wrote:
    > Hi
    >
    > Bo Yang wrote:
    >
    >> 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.
    >


    Provided that the class "Test" has proper copy semantics!
     
    Ron Natalie, Nov 30, 2006
    #12
  13. Bo Yang

    Bo Yang Guest

    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!
     
    Bo Yang, Nov 30, 2006
    #13
  14. On 2006-11-30 15:40, Bo Yang wrote:
    > 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!


    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.

    --
    Erik Wikström
     
    =?ISO-8859-1?Q?Erik_Wikstr=F6m?=, Nov 30, 2006
    #14
  15. Bo Yang

    Gavin Deane Guest

    Erik Wikström wrote:
    > On 2006-11-30 15:40, Bo Yang wrote:
    > > 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!


    <snip useful possible explanation of the behaviour the OP saw>

    > 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
     
    Gavin Deane, Nov 30, 2006
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. sstark
    Replies:
    0
    Views:
    483
    sstark
    Mar 6, 2005
  2. ryang
    Replies:
    1
    Views:
    989
    Wes Groleau
    Apr 11, 2005
  3. Apogee

    Strange Behavior with ViewState

    Apogee, Jul 3, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    338
    Apogee
    Jul 3, 2003
  4. PJ

    DropDownList Strange Behavior

    PJ, Jul 8, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    362
  5. Mantorok Redgormor
    Replies:
    70
    Views:
    1,850
    Dan Pop
    Feb 17, 2004
Loading...

Share This Page