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

时间:2018-07-26 20:47:46

标签: c++ pointers stdvector

以下程序的意外行为(只要我能做到):

#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不会受到破坏。

1 个答案:

答案 0 :(得分:3)

您正在向量中存储一个指向临时变量的指针(特别是在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; }