【问题标题】:How to prevent a temporary from going out of scope?如何防止临时超出范围?
【发布时间】:2017-01-02 02:44:16
【问题描述】:

在以下情况下,我的对象超出范围并且我访问了一个无效的指针:

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)....

【问题讨论】:

  • 我不明白这个问题。您是否要求将不可默认构造的内容放入std::vector
  • reserve() 不是这样做的。
  • “之后我使用我的 vectorOfAnimals[0] 并访问了一个无效的指针” 你对“无效指针”是什么意思?它在哪里?
  • 对不起,我最好重写问题
  • @TitoneMaurice 您想通过使用矢量来防止这些问题,但是 IMO 您只专注于矢量会走错方向。向量刚刚暴露了你班级的问题。要么修复复制问题(通过使用std::vector&lt;char&gt;std::string 很容易完成),或者通过将复制操作设为私有和未实现来使类不可复制,或者使用C++ 11 的= delete 语法。跨度>

标签: c++ vector scope copy raii


【解决方案1】:

问题不在于临时超出范围。真正的问题是 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];
};

【讨论】:

  • 谢谢。这是我不清楚的错。我想学习如何避免复制。我认为有办法在向量中构造对象。
  • @TitoneMaurice 您的问题中没有任何内容甚至暗示您避免复制的目标。在 C++11 之前,您无法避免复制。在 C++11 及更高版本中,您可以使用 emplace_back() 代替 push_back()。但是,当一个类管理不能浅拷贝的资源时,您仍然应该遵循三法则(以及 C++11 及更高版本中的五法则)。
  • vector::emplace_back() 不起作用。当我做 myVector.emplace_back(myStruct(arg1, arg2));它首先构造 myStruct,然后在向量中复制构造一个新的。 Grrrr,一次一件事。如果 emplace_back() 不起作用,我想我应该问一个问题。
  • @TitoneMaurice 移除显式临时变量,只需自己传递构造函数参数即可:myVector.emplace_back(arg1, arg2); emplace_back() 将在就地构造新对象时将参数转发给类型构造函数。
猜你喜欢
  • 1970-01-01
  • 2017-06-20
  • 2015-01-09
  • 2015-12-05
  • 2021-05-14
  • 2011-01-26
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
相关资源
最近更新 更多