简体   繁体   中英

Why converting a pointer into a reference doesn't work in my accessor (getter)?

I have recently learned how to convert a pointer into a reference using this tip . But when I do this in an accessor, it seems to create an undefined behavior, and I can't understand why.

Why I want to do this

I have a class WalkerOwner which owns an instance of the class Walker . This instance of Walker is needed elsewhere in the code, so I have provided an accessor (getter) to the class WalkerOwner , which gives a reference.

class WalkerOwner
{
public:
    ...
    Walker& getWalker() {return m_ownedWalker;} ;
private:
    Walker m_ownedWalker;
};

Later on, I realized that WalkerOwner should actually internally manage a pointer to the walker for some reason. As I don't want to refactor the rest of my code, changing every reference to a pointer, I have tried to convert the pointer into a reference in the getter.

Code which shows the problem

#include <iostream>
#include <string>
using std::string;

class Walker
{
    public:
        Walker() : m_travelledDistance(5) {};
        ~Walker(){};

        void swank() { std::cout << "I have walked " << distanceAsString() << "! How good I am!" <<  std::endl; };

    private:
        // Calling this function makes the result even more impressive
        string distanceAsString() { return std::to_string(m_travelledDistance) + " meters" ; };

        int m_travelledDistance;
};

class WalkerOwner
{
    public:
        WalkerOwner() {m_ownedWalker = new Walker; } ;
        ~WalkerOwner() { delete m_ownedWalker;};

        // I know I should throw an exception if the pointer is not valid.
        Walker& getWalker() {return Walker(*m_ownedWalker);} ; //conversion from pointer into reference

    private:
        Walker* m_ownedWalker;
};


int main(int argc, char *argv[])
{
    // -------
    // Case 1
    // If "main" owns John the walker, everything seems fine.
    // -------
    Walker* ptrToJohn = new Walker ;
    Walker& john = Walker(*ptrToJohn); //conversion from pointer into reference

    john.swank();

    delete ptrToJohn ;

    // -------
    // Case 2
    // If someone else owns Jack the walker, a disaster occurs.
    // -------
    WalkerOwner walkerOwner ;
    Walker& jack = walkerOwner.getWalker() ;

    // (When I put a breakpoint here, my Integrated Devlopment 
    // Environnment says that walker.m_travelledDistance = 5)

    jack.swank(); 

    std::cin.get(); // Press enter to close
    return EXIT_SUCCESS;
}

When I run this code, I get the following output:

I have walked 5 meters! How good I am!

I have walked -858993460 meters! How good I am!

An interesting thing is that a breakpoint shows that jack is in the expected state just before calling the function "swank()". (Humm... Maybe swanking has made him lose his mind!)

Anyway, I would be very glad if someone could explain to me this behavior, and could tell if it is possible to make a safe accessor that performs this conversion.

By writing return Walker(*m_ownedWalker); , you're not "converting" from *m_ownedWalker to a full Walker : you're creating a new Walker using the current one as a source.

Simply return *m_ownedWalker;

As @user2079303 points out: because you're returning a new Walker , and then not saving it anywhere (saving a reference is not the same as saving the actual value), it gets destroyed immediately. And then you derefence the (now-dead, uninitialised-anyway) copy - no wonder garbage was printed out!

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