[英]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) )
也就是說,由於操作員*文檔聲明,您所要求的功能已經就位:
// 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 ctor\n"; }
Foo(const Foo&) { std::cout << "Foo copy ctor\n"; }
Foo(Foo&&) { std::cout << "Foo move ctor\n"; }
~Foo() { std::cout << "~Foo dtor\n"; }
};
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 ctor\n"; }
D(D&) { std::cout << "D non-const copy ctor\n";}
D(D&&) { std::cout << "D move ctor \n"; }
void operator()(InternalPtr& p) const {
std::cout << "D is deleting a Foo\n";
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;
}
}
為了完整起見,我將發布兩個額外的替代方案/解決方法:
指針通過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
公開派生,只需添加這樣的成員函數即可。
但請記住,在任何地方使用這種經過檢查的方法都可能會導致嚴重的性能損失(與at相同)。 通常,您希望最多驗證一次參數,開頭的單個if語句更適合。
還有學校說你不應該拋出異常來回應編程錯誤。 也許負責設計unique_ptr
的peopke屬於這所學校,而設計矢量(更老的人)的人卻沒有。
智能指針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 empty\n";
if (bar) std::cout << "bar points to " << *bar << '\n';
else std::cout << "bar is empty\n";
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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.