std::vector back()的奇怪行为

Odd behavior of std::vector back()

本文关键字:vector back std      更新时间:2023-10-16

以下代码在所示位置断言"迭代器+偏移量超出范围。"

void Network::PushInput(int c, int h, int w) {
    Input* input = new Input(batch, c, h, w, data);
    layers.push_back(input);    // this happens to be the first push_back()
//  layers.push_back(input);    // doing another doesn't change the assert!
    Layer *foo = layers.back();  // asserts here
    Layer *baz = layers[layers.size()-1];  // does not assert
}

Input是Layer的公共子类。层被声明为

std::vector<Layer *>layers;

如果我试图用更多的普通模板类型来复制上面的内容,例如,int*,back()在没有断言的情况下按预期工作。不知怎的,模板类型在这里很重要。(注意:_ITERATOR_DEBUG_LEVEL是2,它触发向量类中的断言检查。)

我不想直接将代码中的所有back()都改为size()-1,而是想了解这里发生了什么。

有什么想法吗?(我会继续扰乱代码,直到我能找到明显的原因,但希望这对其他人来说是显而易见的。)

(如果这很重要的话,我使用的是Visual Studio 2013社区版。)

下面是一个编译的独立文件,显示了问题:

#include <vector>
using namespace std;
namespace layer {
    class Layer {
    public:
        Layer(float alpha = 0, float momentum = 0.9f, float weight_decay = 0);
        virtual ~Layer();
        // three virtual method that all layers should have
        virtual void forward(bool train = true) = 0;
        virtual void backward() = 0;
        virtual void update() = 0;
        void adjust_learning(float scale); // change the learning rate
        Layer* prev;                    // previous layer
        Layer* next;                    // next layer
        float* data;                    // X': output (cuDNN y)
        int batch;                      // n: batch size
        float alpha;                    // learning rate
        float momentum;                 // beta: momentum of gradient
        float weight_decay;             // gamma: weight decay rate
    };
} /* namespace layer */
namespace layer {
    Layer::Layer(float alpha_, float momentum_, float weight_decay_)
    {
        std::memset(this, 0, sizeof(*this));
        alpha = alpha_;
        momentum = momentum_;
        weight_decay = weight_decay_;
    }
    Layer::~Layer() {}
    void Layer::adjust_learning(float scale) {
        alpha *= scale;
    }
}
namespace layer {
    class Input : public Layer {
    public:
        Input(int n, int c, int h, int w, float* _data);
        virtual ~Input();
        void forward(bool train = true);
        void backward();
        void update();
    };
}
namespace layer {
    Input::Input(int n, int c, int h, int w, float* _data) : Layer() {
        prev = NULL;
        batch = n;
        data = _data;
    }
    Input::~Input() {
        data = NULL;
    }
    void Input::forward(bool train) {
        // nothing
    }
    void Input::backward() {
        // nothing
    }
    void Input::update() {
        // nothing
    }
}
using namespace layer;
namespace model {
    class Network {
    private:
        std::vector<Layer*> layers; // list of layers
        bool has_input, has_output; // sanity check
        float* data; // input on device
        int batch; // whole size of data, batch size
    public:
        Network(int batch_size);
        virtual ~Network();
        void PushInput(int c, int h, int w);
    };
}
namespace model {
    void Network::PushInput(int c, int h, int w) {
        Input* input = new Input(batch, c, h, w, data);
        layers.push_back(input);
        Layer *foo = layers.back();  // **WHY DOES THIS ASSERT??**
    }
    Network::Network(int _batch) {
        std::memset(this, 0, sizeof(*this));
        batch = _batch;
    }
    Network::~Network() {
        for (Layer* l : layers)
            delete l;
    }
}
void main()
{
    model::Network foo(10);
    foo.PushInput(2, 3, 4);
}

您的代码中有未定义的行为

Layer构造函数中执行

std::memset(this, 0, sizeof(*this));

问题是,上面的调用也会清除虚拟函数表(它是对象的一部分)。之后调用的任何虚拟函数都将无法按预期工作(如果有的话)。这包括销毁对象,因为销毁程序是虚拟的。