我想实现我自己的
observer_ptr
类,我想用它来表示没有指针的所有权。我想知道是否有可能以某种方式阻止某人使用以下语法:
observer_ptr<T> var_name(new T);
因为它会完全破坏这个类的全部目的并导致内存泄漏。我的猜测是我必须删除/私有化某种类型的构造函数,但我不知道我应该选择哪种参数类型来实现它。
我没有写任何可共享的库,我对自己提出更多要求,以便我可以学习。
如果您通过非常量引用接受输入
T*
指针,用户不能在构造函数调用中直接调用 new
,因为引用将无法绑定到 new
返回的临时值,例如:
template <typename T>
class observer_ptr
{
public:
observer_ptr(T*& ptr);
...
};
observer_ptr<T> var_name(new T); // error!
他们将需要使用本地
T*
变量来代替,使他们更加意识到自己对所创建对象的生命周期负有责任,例如:
T *ptr = new T;
observer_ptr<T> var_name(ptr); // OK!
但是请注意,这种方法确实会阻止用户直接观察地址被
operator&
或std::addressof()
获取的非动态对象,以及unique_ptr
和shared_ptr
对象,例如:
T obj;
observer_ptr<T> var_name(&obj); // error!
auto ptr = make_unique<T>(); // or make_shared()
observer_ptr<T> var_name(ptr.get()); // error!
这也需要使用本地
T*
指针,例如:
T obj;
T* ptr = &obj;
observer_ptr<T> var_name(ptr); // OK!
auto obj = make_unique<T>(); // or make_shared()
T* ptr = obj.get();
observer_ptr<T> var_name(ptr); // OK!
你可以避免,如果你添加一些额外的构造函数到
observer_ptr
,例如:
template <typename T>
class observer_ptr
{
private:
T* m_ptr;
public:
observer_ptr(T*& ptr) : m_ptr(ptr) {}
observer_ptr(T& obj) : m_ptr(addressof(obj)) {}
observer_ptr(const unique_ptr<T>& ptr) : m_ptr(ptr.get()) {}
observer_ptr(const shared_ptr<T>& ptr) : m_ptr(ptr.get()) {}
observer_ptr(unique_ptr<T>&&) = delete;
observer_ptr(shared_ptr<T>&&) = delete;
...
};
T obj;
observer_ptr<T> var_name(obj); // OK!
auto ptr = make_unique<T>(); // or make_shared()
observer_ptr<T> var_name(ptr); // OK!
observer_ptr<T> var_name(make_unique<T>()); // error!
observer_ptr<T> var_name(make_shared<T>()); // error!
不可能对构造函数参数做这样的限制。如果你拿一个指针,它可以是任何指针(兼容类型)。
然而在我看来,这不是你应该用语言强制执行的东西,而是一个契约和类的意图,因为如果你看看智能指针类,它们也接受任何指针类型,并且在它出现时完全依赖文档和常识他们采用什么样的指针:
int stackValue;
std::unique_ptr myPtr{ &stackValue }; // it will be UB upon myPtr destruction but compiles anyway
此外,原始指针
T*
是表达对象不拥有的某些指针的常规方式。所以你可能在这里重新发明轮子并且可能想要重新考虑这个意图。
不管怎样,你都需要能够传递一个(智能)指针或一个引用来创建一个
observer_ptr
,而用户总是能够把它搞砸。您可以通过更明显的方式让标题状态的用户了解他们打算做什么,并希望让他们充分考虑他们正在编写的代码以自己捕获错误。
您可以通过添加一个函数来创建
observer_ptr
并通过可见性限制要求使用它来完成此操作。以下示例要求用户使用 make_unowned_ptr(...)
为空指针以外的任何内容创建 observer_ptr
:
template<class T>
class observer_ptr;
template<class T>
constexpr observer_ptr<T> make_unowned_ptr(T* ptr)
{
return { ptr };
}
template<class T>
class observer_ptr
{
T* m_ptr;
constexpr observer_ptr(T* ptr)
: m_ptr(ptr)
{
}
friend constexpr observer_ptr<T> make_unowned_ptr<T>(T*);
public:
constexpr observer_ptr(std::nullptr_t = nullptr)
: m_ptr(nullptr)
{
}
};
int main()
{
int value = 1;
observer_ptr<int> ptr1(&value); // compiler error: constructor not visible
auto ptr2 = make_unowned_ptr(&value); // ok
}