简体   繁体   中英

Counter-intuitive behavior with virtual functions

Given

#include <string>
#include <iostream>


struct A  {
    virtual operator std::string() const { return "A"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

struct B1 : public A {
    virtual operator std::string() const { return "<"; }
};

struct B2 {
    B2() { }
    virtual ~B2() { }
    virtual operator std::string() const { return ">"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

struct C1 : public A {
    C1() { }
    virtual ~C1() { }
    virtual operator std::string() const { return "["; }
};

struct C2 {
    C2() { }
    virtual ~C2() { }
    virtual operator std::string() const { return "]"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

int main() {
    using namespace std;
    cout << B1() << endl;
    cout << C1();
    cout << C2() << B2() << endl;
}

The output should be "<[]>". However, it is "<[]]".

  1. Am I wrong? If so, why?
  2. If not, then what are the potential reasons for this behavior?

Actually, the behaviour of your code is undefined due to the following:

return this->operator std::string().c_str();

You're calling c_str() on a temporary, and using the result later on.

What you're seeing is a valid manifestation of undefined behaviour.

Now, if you're curious as to what actually happens under the hood, you could modify the last line of main() to read:

    cout << (const void*)C2() << ' ' << (const void*)B2() << endl;

If you do, you'll probably see the same address getting printed twice (which in both cases is a dangling pointer). This is what happens on my computer, and I suspect what happens on yours. Of course, since the behaviour is undefined, this is just one possible manifestation of many.

Your problem really boils down to:

#include <string>
#include <iostream>

struct B2 {
    B2() { }
    virtual ~B2() { }
    virtual operator std::string() const { return ">"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

struct C2 {
    C2() { }
    virtual ~C2() { }
    virtual operator std::string() const { return "]"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

int main() {
    using namespace std;
    cout << C2() << B2() << endl;
}

Where it is evident that this has nothing to do with virtual functions. What happens here is that operator const char* is called, which calls operator std::string which returns a temporary std::string from which you return the .c_str() . Since after the call of operator const char* this temporary std::string is destroyed, you have a const char* pointing to already freed memory.

And now anything can happen, since this is UB...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM