如何防止临时超出范围?

时间:2017-01-02 02:44:16

标签: c++ vector scope copy raii

在下面的例子中,我的对象超出了范围,我访问了一个无效的指针:

struct Animal
{
    char* buffer;
    Animal() { buffer = new char[100]; }
    ~Animal() { delete[]buffer; }
};


int main()
{
    vector<Animal> list;

    {
        Animal dog;
        list.push_back(dog);
    }

    list[0].buffer[50] = 7;  // Buffer's been deleted, can't access it
 }

我想防止这种情况的最好方法是在向量​​中构建Animal对象,但我不知道该怎么做。我想过要做:

list.push_back(Dog());

但这仍然会产生一个暂时的,除非它被优化掉了,我宁愿不依赖它,因为在另一个地方(另一个编译器),它可能不会做同样的事情。

编辑:感谢Remy Lebeau我已经了解到你可以直接在向量中构造一个向量元素,没有临时性,没有复制,具有以下功能:

template< class... Args >
void emplace_back( Args&&... args );

我不知道可变参数模板参数是如何工作的,但描述是:

  

将新元素追加到容器的末尾。元素是   通过std :: allocator_traits :: construct构造,通常是   使用placement-new在该位置就地构造元素   由容器提供。参数args ...被转发给   构造函数为std :: forward(args)....

1 个答案:

答案 0 :(得分:3)

问题不在于临时性超出范围。真正的问题是,Animal违反了Rule of three,没有实现复制构造函数或复制赋值运算符。

当您将临时文件推入向量时,会生成对象的副本,但编译器生成的复制构造函数只是按原样复制指针,它不会分配内存的副本。因此,当临时文件被销毁时,内存将在析构函数中释放,并且副本会留下一个指向无效内存的悬空指针。

添加复制构造函数以分配新内存:

struct Animal
{
    char* buffer;

    Animal() {
        buffer = new char[100];
    }

    Animal(const Animal &src) {
        buffer = new char[100];
        std::copy(src.buffer, src.buffer+100, buffer);
    }

    ~Animal() {
        delete[] buffer;
    }

    Animal& operator=(const Animal &rhs) {
        if (this != &rhs) {
            std::copy(rhs.buffer, rhs.buffer+100, buffer);
        }
        return *this;
    }
};

或者,使用std::vector而不是原始指针,让编译器为您生成合适的复制构造函数,复制赋值运算符和析构函数:

struct Animal
{
    std::vector<char> buffer;
    Animal() : buffer(100) {}
};

或者,只需静态分配内存而不是动态分配:

struct Animal
{
    char buffer[100];
};