为什么没有unique_ptr::operator*()的安全替代方案?
Why is there no safe alternative to unique_ptr::operator*()?
std::vector
具有成员函数at()
作为operator[]
的安全替代,因此应用绑定检查并且不会创建悬空引用:
void foo(std::vector<int> const&x)
{
const auto&a=x[0]; // What if x.empty()? Undefined behavior!
const auto&a=x.at(0); // Throws exception if x.empty().
}
而std::unique_ptr
缺乏相应的功能:
void foo(std::unique_ptr<int> const&x)
{
const auto&a=*x; // What if bool(x)==false? Undefined behavior!
}
如果std::unique_ptr
有这样一个安全的替代,比如成员ref()
(和cref()
),它从不返回悬空引用,而是抛出异常,那就太好了。可能的实现:
template<typename T>
typename add_lvalue_reference<T>::type
unique_ptr<T>::ref() const noexcept(false)
{
if(bool(*this)==false)
throw run_time_error("trying to de-refrence null unique_ptr");
return this->operator*();
}
有什么好的理由说明为什么标准不提供这种东西吗?
unique_ptr
被专门设计为具有空状态检测的轻量级指针类(例如,在一个添加实用程序类来表示可选对象的建议(修订3)中声明为optional)
也就是说,您所要求的功能已经存在,因为操作符*文档声明:
// may throw, e.g. if pointer defines a throwing operator*
typename std::add_lvalue_reference<T>::type operator*() const;
pointer
类型定义为
std::remove_reference<Deleter>::type::pointer if that type exists, otherwise T*
因此,通过您的自定义删除器,您可以执行任何动态操作,包括空指针检查和抛出异常
#include <iostream>
#include <memory>
struct Foo { // object to manage
Foo() { std::cout << "Foo ctorn"; }
Foo(const Foo&) { std::cout << "Foo copy ctorn"; }
Foo(Foo&&) { std::cout << "Foo move ctorn"; }
~Foo() { std::cout << "~Foo dtorn"; }
};
struct Exception {};
struct InternalPtr {
Foo *ptr = nullptr;
InternalPtr(Foo *p) : ptr(p) {}
InternalPtr() = default;
Foo& operator*() const {
std::cout << "Checking for a null pointer.." << std::endl;
if(ptr == nullptr)
throw Exception();
return *ptr;
}
bool operator != (Foo *p) {
if(p != ptr)
return false;
else
return true;
}
void cleanup() {
if(ptr != nullptr)
delete ptr;
}
};
struct D { // deleter
using pointer = InternalPtr;
D() {};
D(const D&) { std::cout << "D copy ctorn"; }
D(D&) { std::cout << "D non-const copy ctorn";}
D(D&&) { std::cout << "D move ctor n"; }
void operator()(InternalPtr& p) const {
std::cout << "D is deleting a Foon";
p.cleanup();
};
};
int main()
{
std::unique_ptr<Foo, D> up(nullptr, D()); // deleter is moved
try {
auto& e = *up;
} catch(Exception&) {
std::cout << "null pointer exception detected" << std::endl;
}
}
<<p> 生活例子/kbd> 为了完整起见,我将发布两个额外的替代方案/解决方案:
通过
operator bool
检查unique_ptr
的指针#include <iostream> #include <memory> int main() { std::unique_ptr<int> ptr(new int(42)); if (ptr) std::cout << "before reset, ptr is: " << *ptr << 'n'; ptr.reset(); if (ptr) std::cout << "after reset, ptr is: " << *ptr << 'n'; }
(这可能是处理这个问题最清晰的方法)
另一个解决方案,虽然比较混乱,是使用一个包装器类型来处理异常
我想真正的答案很简单,对于许多"为什么c++不是这样的?"的问题也是一样的:
没人提议。
std::vector
和std::unique_ptr
不是同一个人在同一时间设计的,使用方式也不一样,所以不一定要遵循相同的设计原则。
我不能说,为什么委员会决定不添加安全的解引用方法-答案可能是"因为没有提出"或"因为原始指针也没有一个"。但是,自己编写一个自由函数模板,将任何指针作为参数,将其与nullptr进行比较,然后抛出异常或返回指向对象的引用,这是微不足道的。
如果不通过指向基类的指针删除它,甚至可以从unique_ptr
公开派生,并添加这样的成员函数。
也有学派认为不应该抛出异常来响应编程错误。也许负责设计unique_ptr
的人属于这个学派,而设计vector的人(要老得多)不是。
智能指针API设计的主要目标之一是成为具有附加价值的即时替代品,没有漏洞或副作用,并且接近于零开销。if (ptr) ptr->...
是对裸指针的安全访问通常是如何完成的,同样的语法可以很好地用于智能指针,因此当一个指针被另一个指针替换时,不需要更改代码。
放置在指针内的额外有效性检查(例如抛出异常)将干扰分支预测器,从而可能对性能产生连锁反应,这可能不再被认为是零成本的替代。
你有
operator bool()
的例子:cplusplusreference
// example of unique_ptr::operator bool
#include <iostream>
#include <memory>
int main () {
std::unique_ptr<int> foo;
std::unique_ptr<int> bar (new int(12));
if (foo) std::cout << "foo points to " << *foo << 'n';
else std::cout << "foo is emptyn";
if (bar) std::cout << "bar points to " << *bar << 'n';
else std::cout << "bar is emptyn";
return 0;
}
unique_ptr是对原始指针的简单包装,当您可以轻松地检查布尔条件时,不需要抛出异常。
编辑:显然操作员*可以抛出。
异常1)可以抛出,例如,如果指针定义了抛出操作符*
也许有人可以解释一下如何定义一个抛出操作符*
根据MikeMB的建议,这里有一个可能实现的自由函数,用于解引用指针和unique_ptr
。
template<typename T>
inline T& dereference(T* ptr) noexcept(false)
{
if(!ptr) throw std::runtime_error("attempt to dereference a nullptr");
return *ptr;
}
template<typename T>
inline T& dereference(std::unique_ptr<T> const& ptr) noexcept(false)
{
if(!ptr) throw std::runtime_error("attempt to dereference an empty unique_ptr)");
return *ptr;
}
- 从不同线程使用int64的不同字节安全吗
- 运行同一解决方案的另一个项目的项目
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- Project Euler问题4的错误解决方案
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 计算每个节点的树高,帮助我解释这个代码解决方案
- EOF有更安全的替代方案吗?它在我的情况下不起作用
- 对于 ~95% 写入/5% 读取线程安全的无序列图,有没有一个简单的解决方案?
- vtkUnstructuredGrid->GetPoint() 的线程安全只读替代方案
- 我可以使用模板作为多态处理数组的安全解决方案
- 安全复制对象的替代方案
- std::map的线程安全替代方案
- 在这种情况下,这是最优雅/最安全/推荐的解决方案吗?
- c++:跨平台且安全的系统(…)的替代方案是什么?
- 为什么没有unique_ptr::operator*()的安全替代方案?
- 黑客攻击线程安全是"最佳"行动方案吗?