创建自己的 owner_ptr class;传递堆栈或静态分配地址时如何避免 UB?

Creating own owner_ptr class; how to avoid UB when passing stack or statically allocated addresses?

我想在我的代码中使用自己的指针-classes 而不是原始指针以使我的代码更清晰。所以我决定实现一个模板化的 owner_ptr class 应该满足以下要求:

我有以下代码:

#include <cstddef>

template <typename T>
class owner_ptr
{
    T* ptr;
    bool array;

public:
    owner_ptr() : ptr(NULL) {}
    owner_ptr(T* ptr, bool isArray = false) : ptr(ptr), array(isArray) {}
    owner_ptr(owner_ptr<T>& orig) : ptr(orig.ptr), array(orig.array)
    {
        orig.ptr = NULL;
        /* rvalue loses ownership over the object */
    }

    ~owner_ptr()
    {
        if (ptr != NULL)
        {
            if (!array)
            {
                delete ptr;
            }
            else
            {
                delete[] ptr;
            }
        }
    }

    bool null() { return ptr == NULL; }

    owner_ptr& operator=(owner_ptr<T>& rvalue)
    {
        if (this != &rvalue)
        {
            this->~owner_ptr();
            ptr = rvalue.ptr;
            array = rvalue.array;
            rvalue.ptr = NULL;
            /* rvalue loses ownership again */
        }
        return *this;
    }

    void reset()
    {
        this->~owner_ptr();
        ptr = NULL;
    }

    void addPtr(T* newPtr, bool isArray = false)
    {
        this->~owner_ptr();
        ptr = newPtr;
        array = isArray;
    }

    T& operator*() { return *ptr; }
    const T& operator*() const { return *ptr; }

    T* operator->() { return ptr; }
    const T* operator->() const { return ptr; }

    T& operator[](int i) { return ptr[i]; }
    const T& operator[](int i) const { return ptr[i]; }
};

用户有责任创建合法的owner_ptr对象,例如:

owner_ptr<int> op1;
op1.addPtr(new int[2], true);
/* OR */
owner_ptr<int> op2(new int);
/* OR */
owner_ptr<int> op3(new int[6], true);
/* OR */
owner_ptr<int> op4(op3);
/* OR */
owner_ptr<int> op5;
op5 = op4;

尽管我指定这是用户的责任,而且我知道用户可能会犯很多错误,这可能会导致 未定义的行为,例如:

owner_ptr<double> op6;
{
   double something;
   op6.addPtr(&something);
}
std::cout << *op6 << std::endl;
/* causes undefined behaviour */

但是,有没有办法禁止传递静态分配的地址和以下参数?

owner_ptr<int> p1(new int);
owner_ptr<int> p2(p1.operator->()); /* <- how to disable this? */
owner_ptr<int> p3(&p1.operator*()); /* <- and this */
/* problem: p1 and p2 (and p3) now own the same object */

你知道 C++98 中可用的想法吗? 或者更简单:如果用户将指针传递给使用 new 创建的构造函数,是否有一种方法可以使用宏进行测试?我知道这是一个丑陋的想法,但对我来说没问题。

请考虑到我这样做是出于学习目的。当然,我不会在生产代码中使用我自己的智能指针classes。

is there a way to disable the following syntax?

owner_ptr<int> p1(new int);

我能想到的一个简单方法是隐藏

owner_ptr(T* ptr, bool isArray = false)

来自 public 部分的构造函数,并具有 friend 仅创建有效实例的函数

template<typename T>
owner_ptr<T> make_owner_ptr(/* T constructor arguments to forward */) {
    return owner_ptr<T>(new T(/* forwarded constructor arguments */));
}

你用c++98遇到的一个问题是如何实现构造函数参数转发。 va_args 不是一个很好的解决方案,因为您至少需要一个参数计数哨兵。
也许可以使用一些宏技巧。我建议看看 boost::shared_ptr 是如何实现的。

owner_ptr<int> p2(p1.operator->()); /* <- how to disable this? */

恐怕这是不可能的。但是我从上面的解决方案总体上避免了。


Of course, I won't use my own smart pointer classes in production code.

为什么不呢?我在需要时做了很多次(例如,为了覆盖 c++11 之前的引用计数智能指针 类)。我发现 A. Andrescou 的 loki 图书馆对此很有帮助。
这完全不是传闻中的那种黑魔法。有一些注意事项和直接的限制和规则。