通过原始指针访问的向量元素意外更改值

vector element, accessed through raw pointer, unexpectedly changes value

以下程序的意外行为(尽可能短):

#include <vector>
#include <iostream>

class Entry;

class CachedPayload {
public:
    CachedPayload(const Entry* const o) : owner_(o) {}
    void info(const char* where);
private:
    const Entry* const owner_;
    // meaningful payload not shown in this minimal example
};

class Entry {
public:
    Entry(int i) : cached(this), my_i(i) {}
    void showEntry(const char* where) const {
        std::cout << where << ": this=" << this << ", i=" << my_i << std::endl; }
    CachedPayload cached;
private:
    int my_i;
};

void CachedPayload::info(const char* where) { owner_->showEntry(where); }

class CachingVector {
public:
    CachingVector() {
        resize();
        data_.at(0).cached.info("GET");
    }
    void resize() {
        data_.push_back(Entry(97));
        data_.at(0).cached.info("RSZ");
    }
private:
    std::vector<Entry> data_;
};

int main()
{
    CachingVector vec;
}

结果:

$ clang++-6.0 h.cpp && a.out
RSZ: this=0x7ffe1dc52dc8, i=97
GET: this=0x7ffe1dc52dc8, i=4206609

$ g++ h.cpp && a.out
RSZ: this=0x7ffc5e977040, i=97
GET: this=0x7ffc5e977040, i=-578764228

为什么 vec.data_[0].my_i 的值在通过 vec.data_[0].cached.owner_ 访问时以一种无意义的、依赖于编译器的方式被覆盖?

当我将 CachingVector::resize() 的两行实现合并到构造函数 CachingVector() 中时,i=97 保持完好。

您正在向量中存储一个指向临时对象的指针(特别是在 CachedPayload::owner_ 中)。这就是问题的原因。一旦临时文件被销毁,你就会有一个悬空指针。

我已将此添加到您的代码中

~Entry() { std::cout << "destroyed " << this << std::endl; }

还有这个

CachingVector() {
    data_.reserve(1); // prevent reallocation
    resize();
    data_.at(0).cached.info("GET");
}

新输出是

destroyed 00000000001AF588
RSZ: this=00000000001AF588, i=97
GET: this=00000000001AF588, i=-858993460

如您所见,您正在访问一个已被销毁的对象,这当然是未定义的行为。

要解决此问题,我认为您只需要为 Entry 定义一个复制构造函数和赋值运算符,以确保 cached.owner_ 始终具有正确的值。

Entry(const Entry& rhs) : cached(this), my_i(rhs.my_i) {}
Entry& operator=(const Entry& rhs) { my_i = rhs.my_i; return *this; }