【问题标题】:invalid read on pointer returned from get()从 get() 返回的指针上的无效读取
【发布时间】:2018-03-31 18:20:49
【问题描述】:

C++ 新手,试图理解对象生命周期和智能指针。

为什么以这种方式使用 get() 从 unique_ptr 创建原始指针的结果:

auto w = (std::make_unique<Wall>()).get();

导致指针通过空检查但在使用时导致无效读取(根据 valgrind 看起来像是由 Wall 析构函数中的双重删除引起的?)但以两步方式创建时:

auto wall = std::make_unique<Wall>();
auto w = wall.get();  

没有这样的问题?

所以我有两个问题:

(1) 为什么第一种方式会导致原始指针导致读取无效,而第二种方式不会?

(2)为什么无效的读指针会通过空检查?

#include <iostream>
#include <memory>
#include <string>

class Object {
public:
    virtual ~Object() = 0;
    virtual void talk() {}
};

Object::~Object() {}

class Wall: public Object {
public:
    virtual ~Wall() {}
    virtual void talk() { std::cout << "I'm a wall." << std::endl; }
};

int main(int argc, char** argv) {

    //auto wall = std::make_unique<Wall>();
    //auto w = str.get();  
    auto w = (std::make_unique<Wall>()).get();

    if (!w) 
        std::cout << "null pointer..." << std::endl;

    w->talk();
}  

valgrind 输出:

    ==21668== Memcheck, a memory error detector
    ==21668== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==21668== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
    ==21668== Command: ./wall
    ==21668== 
    ==21668== Invalid read of size 8
    ==21668==    at 0x402452: main (main.cpp:33)
    ==21668==  Address 0x5ab6c80 is 0 bytes inside a block of size 8 free'd
    ==21668==    at 0x4C2F24B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==21668==    by 0x402548: Wall::~Wall() (main.cpp:15)
    ==21668==    by 0x402955: std::default_delete<Wall>::operator()(Wall*) const (unique_ptr.h:76)
    ==21668==    by 0x40269C: std::unique_ptr<Wall, std::default_delete<Wall> >::~unique_ptr() (unique_ptr.h:236)
    ==21668==    by 0x40242A: main (main.cpp:27)
    ==21668==  Block was alloc'd at
    ==21668==    at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==21668==    by 0x4025CD: _ZSt11make_uniqueI4WallIEENSt9_MakeUniqIT_E15__single_objectEDpOT0_ (unique_ptr.h:765)
    ==21668==    by 0x40240E: main (main.cpp:27)

【问题讨论】:

  • unique_ptr 被破坏时你认为会发生什么?
  • 为什么你首先要提取原始指针?
  • 为什么需要这个? auto w = new Wall(); 怎么了?
  • @SeverinPappadeux 主要是它缺乏有保证和明确的所有者。
  • “我想使用从 unique_ptr 中提取原始指针作为使用托管指针并在不使用移动的情况下传递它的一种方式。”好吧,那不是它的工作原理。当您提取它时,它不再“受管理”。如果您不想移动,请通过 const 引用传递,或使用 shared_ptr。

标签: c++ c++14 unique-ptr


【解决方案1】:

auto w = (std::make_unique<Wall>()).get();

匿名std::make_unique&lt;Wall&gt;() 只存在到行尾。然后它超出范围并被销毁,并带走了包含的指针。

这会在w 不知道的情况下从w 下删除指针。 w 通过了空检查,因为它不为空,但不能使用它指向的位置。

在你的第二个例子中

auto wall = std::make_unique<Wall>();
auto w = wall.get();  

wall 维护 unique_ptr 直到 wall 超出范围,希望足够长以使用 w

我的建议是放弃w 并始终使用wall。这样就没有悬空指针的机会了。

#include <iostream>
#include <memory>
#include <string>

class Object {
public:
    virtual ~Object() = 0;
    virtual void talk() {}
};

Object::~Object() {}

class Wall: public Object {
public:
    virtual ~Wall() {}
    virtual void talk() { std::cout << "I'm a wall." << std::endl; }
};

int main(int argc, char** argv) {

    auto wall = std::make_unique<Wall>();

    if (!wall)
        std::cout << "null pointer..." << std::endl;

    wall->talk();
    wall.reset();
    if (!wall)
        std::cout << "null pointer..." << std::endl;
}

预期输出:

I'm a wall.
null pointer...

【讨论】:

  • 这就是它通过空检查的原因...因为它确实指向一个地址,但该地址现在无效,是吗?
  • 你明白了。正在更新答案。
  • wall.release(); 泄漏。请改用wall = nullptr;(或wall.reset();
  • @milleniumbug !谢谢!完全错误的功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-23
  • 2017-05-01
  • 2020-10-16
相关资源
最近更新 更多