简体   繁体   中英

Why is a string literal being over written here (C++)?

I have a class holding a pointer. I have only included skeleton code here. The class constructor expects a string.

#include <iostream>
#include <string>

using namespace std;

class slice {
public:
slice(string s):
    data_(s.data()),
    size_(s.size()) {}

    const string toString() {
        string res(data_, data_+size_);
        return res;
    }

private:
    const char* data_;
    const size_t size_;
};

int main() {

    slice a{"foo"};
    slice b{"bar"};


    cout << a.toString() << endl;
    cout << b.toString() << endl;
}

The output of this program is:

$ g++ test.cpp && ./a.out 
bar
bar

I am expecting

foo
bar

What is going on here? How is the pointer held by object a being overwritten?

What is going on here? How is the pointer held by object a being overwritten?

You are experiencing undefined behavior .

slice(string s):
    data_(s.data()),
    size_(s.size()) {}

Here string s is a copy of the input string and lives for the duration of the constructor. Hence s.data() dangles after the constructor is finished.

a and b are being created from temporary string objects. When those string objects are destroyed by the end of the statement, they deallocate their memory, and the underlying char* pointing to where the characters were becomes invalid. You can't usually use a char* taken from a string like this.

The std::string objects calls its destructor std::~string() when it goes out of scope.

slice(string s) : data_(s.data()), size_(s.size()) {
    // After this function is called, the string goes out of scope and s.data() is deallocated by the destructor.
    // Leaving gibberish value inside of 'data_' member
}

Afterwards, when you actually print the value, data_ was already destroyed by then and what you do with it afterwards results in Undefined Behavior (It means the behavior of the program when outputting the string can be anything, thus the term undefined ).


Since this question has already been answered. I might as well give a reasonable solution.

Solution: Create a copy of what the string is holding temporarily inside your class and destroy it when your class goes out of scope:

Example:

class slice {
public:
    slice(string s):
        size_(s.size()) {
        // Dynamically allocate a new string so that it doesn't get deleted when it goes out of scope
        data_ = new char[s.size()];
        // Copy the data into the newly allocated string
        s.copy(data_, s.size());
    }

    slice(const slice&) = default; // Copy constructor
    slice(slice&&) = default;      // Move constructor

    // Destructor
    ~slice() {
        delete[] data_;
    }

    const string toString() {
        // Now 'data_' retains its value even after the string is destroyed
        string res(data_, data_+size_);
        return res;
    }

private:
    char* data_;
    const size_t size_;
};

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