代码之家  ›  专栏  ›  技术社区  ›  Alexis Wilke

我是否在这个通用的唯一删除程序中正确地使用指针类?

  •  3
  • Alexis Wilke  · 技术社区  · 6 年前

    unique_ptr<>() 子类型允许 Deleter delete ptr .

    -O0 -O3 T & operator * () 0 而不是 f_pointer

    // RAII Generic Deleter -- allow for any type of RAII deleter
    //
    // To break compile with:
    //     g++ --std=c++14 -O3 -DNDEBUG ~/tmp/b.cpp -o b
    
    #include <memory>
    #include <iostream>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    template<class T, T null_value, class D, D deleter>
    class raii_generic_deleter
    {
    public:
        class pointer
        {
        private:
            T f_pointer = null_value;
    
        public:
            pointer(T p)
                : f_pointer(p)
            {
            }
    
            pointer(std::nullptr_t = nullptr)
                : f_pointer(null_value)
            {
            }
    
            explicit operator bool () const
            {
                return f_pointer != null_value;
            }
    
            bool operator == (pointer const rhs) const
            {
                return f_pointer == rhs.f_pointer;
            }
    
            bool operator != (pointer const rhs) const
            {
                return f_pointer != rhs.f_pointer;
            }
    
            T & operator * ()
            {
                return f_pointer;
            }
        };
    
        void operator () (pointer p)
        {
            deleter(*p);
        }
    };
    
    
    typedef std::unique_ptr<int,
                raii_generic_deleter<int, -1, decltype(&::close), &::close>>
                            raii_fd_t;
    
    
    int main(int argc, char * argv [])
    {
        int fd = -1;
    
        {
            raii_fd_t safe_fd;
    
            std::cout << "default initialization: safe_fd = " << *safe_fd
                      << std::endl;
    
            fd = open("/tmp/abc.tmp", O_RDWR | O_CREAT, 0700);
    
            std::cout << "fd = " << fd << std::endl;
    
            safe_fd.reset(fd);
    
            std::cout << "safe_fd after the reset(" << fd
                      << ") = " << *safe_fd << std::endl;
        }
    
        if(fd != -1)
        {
            // assuming the safe_fd worked as expected, this call returns an error
            //
            int r = close(fd);
            int e(errno);
    
            std::cout << "second close returned " << r
                      << " (errno = " << e << ")" << std::endl;
        }
    
        return 0;
    }
    

    (原件见 raii_generic_deleter.h

    (无优化):

    default initialization: safe_fd = -1
    fd = 3
    safe_fd after the reset(3) = 3
    second close returned -1 (errno = 9)
    

    *safe_fd 回电 -1 3 T & pointer::operator * ()

    -O1 -O2 ,请 )输出如下:

    default initialization: safe_fd = 0
    fd = 3
    safe_fd after the reset(3) = 0
    second close returned -1 (errno = 9)
    

    而不是 0

            T & operator * ()
            {
                std::cout << "f_pointer within operator * = " << f_pointer
                          << std::endl;
                return f_pointer;
            }
    

    f_pointer within operator * = -1
    default initialization: safe_fd = -1
    fd = 3
    f_pointer within operator * = 3
    safe_fd after the reset(3) = 3
    f_pointer within operator * = 3
    second close returned -1 (errno = 9)
    

    这可能是因为特定的函数没有得到完全优化。

    我在Ubuntu 16.04上用stock g++进行了测试

    bug on the GNU website

    2 回复  |  直到 6 年前
        1
  •  1
  •   n. m. could be an AI    6 年前

    问题似乎是由于libstdc++实现 unique_ptr::operator*

    struct pointer
    {
        pointer(int val = -42) : z(val) { }
        int z = -42;
        int& operator*() { return z; }
    };
    
    struct my_unique_ptr
    {
        pointer rep;
        pointer get() { return rep; }
    #ifdef PROBLEM
        int& operator*() { return *get(); } // libstdc++ implementation
    #else
        int& operator*() { return *rep; } // libc++ implementation
    #endif
    };
    
    int main()
    {
        my_unique_ptr q;
        std::cout << *q << "\n";
    }
    

    pointer operator* 指针

    unique_ptr::operator*() *get()

    立即解决方法是停止定义 unique_ptr 不需要它(不需要NullablePointer来提供它)。

    T operator T() get() 唯一指针

        2
  •  0
  •   Alexis Wilke    6 年前

    我正在添加我自己的答案,为其他感兴趣的RAII删除程序显示代码中的更改(非常实用,因为您不需要每次进行实例化时都指定删除程序!).

    int T & operator * () operator [] () operator -> () 也没有意义)。

    get() operator * () unique_ptr<>() 实施。看起来是这样的:

    /// Return the stored pointer.
    pointer
    get() const noexcept
    { return std::get<0>(_M_t); }
    

    获取() pointer 尽管如此:

    /// Dereference the stored pointer.
    typename add_lvalue_reference<element_type>::type
    operator*() const
    {
      _GLIBCXX_DEBUG_ASSERT(get() != pointer());
      return *get();
    }
    

    operator T () const 而不是 检索

    // RAII Generic Deleter -- allow for any type of RAII deleter
    //
    // To break compile with:
    //     g++ --std=c++14 -O3 -DNDEBUG ~/tmp/b.cpp -o b
    
    #include <memory>
    #include <iostream>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    template<class T, T null_value, class D, D deleter>
    class raii_generic_deleter
    {
    public:
        class pointer
        {
        private:
            T f_pointer = null_value;
    
        public:
            pointer(T p)
                : f_pointer(p)
            {
            }
    
            pointer(std::nullptr_t = nullptr)
                : f_pointer(null_value)
            {
            }
    
            explicit operator bool () const
            {
                return f_pointer != null_value;
            }
    
            bool operator == (pointer const rhs) const
            {
                return f_pointer == rhs.f_pointer;
            }
    
            bool operator != (pointer const rhs) const
            {
                return f_pointer != rhs.f_pointer;
            }
    
            operator T () const
            {
                return f_pointer;
            }
        };
    
        void operator () (pointer p)
        {
            deleter(static_cast<T>(p));
        }
    };
    
    
    typedef std::unique_ptr<int,
                raii_generic_deleter<int, -1, decltype(&::close), &::close>>
                            raii_fd_t;
    
    
    int main(int argc, char * argv [])
    {
        int fd = -1;
    
        {
            raii_fd_t safe_fd;
    
            std::cout << "default initialization: safe_fd = " << safe_fd.get()
                      << std::endl;
    
            fd = open("/tmp/abc.tmp", O_RDWR | O_CREAT, 0700);
    
            std::cout << "fd = " << fd << std::endl;
    
            safe_fd.reset(fd);
    
            std::cout << "safe_fd after the reset(" << fd
                      << ") = " << safe_fd.get() << std::endl;
        }
    
        if(fd != -1)
        {
            // assuming the safe_fd worked as expected, this call returns an error
            //
            int r = close(fd);
            int e(errno);
    
            std::cout << "second close returned " << r
                      << " (errno = " << e << ")" << std::endl;
        }
    
        return 0;
    }
    

    所以主要有两个变化(1) 运算符t()常量 main() ,更改 *safe_fd safe_fd.get() .

    f_pointer T close(int) T