具有自定义指针类型的unique_ptr:*get() 和运算符*() 给出不同的输出

unique_ptr with custom pointer type: *get() and operator*() give different output

本文关键字:运算符 输出 自定义 unique 指针 ptr get 类型      更新时间:2023-10-16

根据cpp首选项调用std::unique_ptr::operator*()相当于调用*(std::unique_ptr::get())

但是,这两个电话都得到了不同的结果。这是我的代码:

#include <iostream>
#include <string>
#include <memory>
#include <fcntl.h>
#include <unistd.h>
struct file_descriptor
{
private:
  struct closer;
public:
  typedef int handle_type;
  typedef closer closer_type;
  constexpr static handle_type kInvalidHandle = -1;
public:
  file_descriptor(int handle = kInvalidHandle) : handle_{ handle } { }
  file_descriptor(std::nullptr_t) : file_descriptor{ } { }
  operator int&() { return handle_; }
  operator int() const { return handle_; }
  int& operator*() { return static_cast<int&>(*this); }
  int operator*() const { return static_cast<int>(*this); }
  bool operator==(const file_descriptor& other) const
  { return (handle_ == other.handle_); }
  bool operator!=(const file_descriptor& other) const
  { return !(*this == other); }
private:
  struct closer
  {
    typedef file_descriptor pointer;
    void operator()(pointer handle) const
    { ::close(*handle); }
  };
  int handle_;
};
using unique_file_ptr = std::unique_ptr<typename file_descriptor::handle_type,
                                        typename file_descriptor::closer_type>;
unique_file_ptr managed_open(const std::string& path)
{
  return { ::open(path.c_str(), O_RDWR), { } };
}
int main(int, char**)
{
  auto handle = managed_open("/dev/random");
  std::cout << "*handle      : " << *handle << std::endl;
  std::cout << "*handle.get(): " << *handle.get() << std::endl;
}

我的输出(此处为实时输出(:

*handle      : 4198400
*handle.get(): 3

请注意,*handle.get()返回正确的值,而*handle则不返回。

为什么我得到不同的结果?

这是发生的事情。 unique_ptr<T, D>::get()返回D::pointer - 在您的情况下,从int句柄构造的临时file_descriptor。然后它的operator*调用它的operator int&,该返回对该临时存储handle_的引用。

直接调用 *handle.get() 时,将在临时死亡之前使用它生成的int&引用。

但是当你打电话给*handle时,你打电话给handle.operator*(),而又叫handle.get()operator*的实现 ,一切都扩展了,变成这样:

int& operator*() {
  file_descriptor temp(internal_pointer_);
  int& result = temp.handle_;
  return result;
}

该函数返回对临时成员的引用。该临时在右大括号处死亡,并且引用变得悬空,因此程序在其生存期结束后通过访问对象而表现出未定义的行为。

我建议你重新考虑解决方案。这里有一种可以说是更干净的方法:

#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
#include <utility>
#include <fcntl.h>
#include <unistd.h>
struct managed_file {
    managed_file() noexcept {};
    managed_file(const std::string& path) 
    : _handle { ::open(path.c_str(), O_RDWR) }
    {
        if (_handle == -1) {
            throw std::runtime_error { std::string { "failed to open file " } + path };
        }
    }
    managed_file(const managed_file&) = delete;
    managed_file(managed_file&& other) noexcept 
    : _handle { other._handle }
    {
        other._handle = -1;
    }
    managed_file& operator=(const managed_file&) = delete;
    managed_file& operator=(managed_file&& other) noexcept {
        managed_file tmp { std::move(other) };
        using std::swap;
        swap(_handle, other._handle);
        return *this;
    }
    virtual ~managed_file() noexcept 
    {
        close();
    }
    void close() noexcept {
        if (_handle != -1) {
            ::close(_handle);
            _handle = -1;
        }
    }
    const int& operator*() const noexcept {
        return _handle;
    }
private:
    int _handle = -1;
};
managed_file managed_open(const std::string& path)
{
    return managed_file { path };
}
using namespace std;
int main(int, char**)
{
    cout << "opening" << endl;
    auto handle = managed_open("/dev/random");
    cout << "checking" << endl;
    std::cout << "*handle      : " << *handle << std::endl;
}