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



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

  • 可以使用使用new关键字创建的原始指针对其进行初始化。
  • 当owner_ptr对象超出范围时,它必须释放分配的内存。
  • 超过 1 个owner_ptr对象不得拥有相同的已分配对象。

我有以下代码:

#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创建的构造函数,有没有办法使用宏进行测试?我知道这是一个丑陋的想法,但对我来说没关系。

请注意,我这样做是为了学习目的。当然,我不会在生产代码中使用自己的智能指针类。

有没有办法禁用以下语法?

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? */

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


当然,我不会在生产代码中使用自己的智能指针类。

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

最新更新