簡體   English   中英

如何返回不可用的常量引用

[英]How to return a const reference which is not available

假設我有以下功能:

const std::string& Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  return kitty->getName();
}

Kitten::getName返回const std::string&我如何最好地處理kittynullptr 我可以返回std::string("")但隨后我返回了對臨時且實際上保證未定義行為的引用。 我可以更改getKittenName函數以返回一個std::string來解決這個問題,但隨后我為所有kitty可用的情況引入了一個冗余副本。 現在我覺得最好的選擇是:

const std::string& Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  if (kitty)
  {
    return kitty->getName();
  }
  static std::string empty("");
  return empty;
}

唯一的問題可能是“魔術靜力學”不可用。 這個解決方案有什么問題,或者有更好的方法嗎?

你有幾個選擇,真的。

  • 最簡單的一個是返回std::string ,但是你提到你出於性能原因不希望這樣做。 我會說你應首先進行分析,以確保它會出現明顯的性能問題,因為所有其他解決方案都會使代碼更復雜,因此至少有一點難以維護。 但是,讓我們說它看起來確實很重要。

  • 如果您擔心未實現線程安全的函數范圍靜態,則可以將后備值創建為Cat的靜態成員:

     class Cat { static const std::string missingKittenName; public: const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else return missingKittenName; } }; 
  • 由於Kitten::getName()顯然返回一個引用(否則你不會擔心副本),你也可以返回一個指針:

     const std::string* Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return &kitty->getName(); else return nullptr; } 
  • 您可以返回對字符串的可選引用:

     boost::optional<const std::string&> Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else return boost::none; } 

    從C ++ 17開始, optionalstd::optional的標准庫的一部分,因此不再需要依賴Boost。

  • 如果缺少名稱這一事實是異常情況(錯誤),您可以拋出異常:

     const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else throw std::invalid_argument("Missing kitten"); } 

返回對const static std :: string的引用。

原因:

  • “魔法靜力學”並不神奇,它們是c ++標准的一部分。
  • 靜態是在代碼第一次流過它們時構建的(即曾經一次)
  • 從c ++ 11開始,靜態構造是線程安全的。
  • 靜態對象在程序結束時以正確的順序正確釋放
  • 一個冗余靜態對象的性能損失完全可以忽略不計,並且比測試null的返回指針的成本要低得多。

如果你在pre-c ++ 11編譯器上有多線程,那么你需要編寫一個線程安全的單例來制作默認字符串,或者在文件范圍內定義它。

C ++ 11:

const std::string& Cat::getKittenName() const
{
  static const std::string noname { /* empty string */ };
  Kitten* kitty = getKitty();
  if (kitty)
  {
    return kitty->getName();
  }
  return noname;
}

C ++ 03:

namespace {
    const std::string noname;
}

const std::string& Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  if (kitty)
  {
    return kitty->getName();
  }
  return noname;
}

返回表示成功/失敗的布爾值,並通過指針傳遞輸出。

這是一個例子:

bool Cat::getKittenName(std::string *name) const {
  Kitten* kitty = getKitty();
  if (kitty) {
    *name = kitty->getName();
    return true;
  }
  return false;
}

您將使用如下:

std::string name;
if(mycat.getKittenName(&name)) {
   // name is populated and is valid
}

這要求由指針傳入的類具有有效的復制構造函數和復制賦值運算符。

我喜歡這樣做有幾個原因:

  1. 返回指針令人困惑,調用者不清楚如何處理指針。

例如上面列出的示例:

const std::string* Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  if (kitty)
    return &kitty->getName();
  else
    return nullptr;
}

將被稱為這樣:

const std::string *name = mykitty.getKittenName();

好的..現在怎么樣? 我無法使用此指針更改name的值,但是我可以更改指針指向的內容嗎? 我應該把它包裝成一個

std::unique_ptr<const std::string>

?? 一般來說,返回指針非常C-esque,並且是一個很大的內存泄漏和錯誤來源,除了令人困惑。

  1. 拋出異常

這個例子:

const std::string& Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  if (kitty)
    return kitty->getName();
  else
    throw std::invalid_argument("Missing kitten");
}

我不是粉絲,因為它鼓勵為非特殊行為拋出異常。 例如,如果你在一組小貓上寫了一個名為'find()'的方法。 沒有找到特定名字的小貓並不是“例外”。 STL使用各種機制,如std :: end和std :: pair,以避免為非異常行為拋出異常。

這產生的另一個問題是,與Java不同,它並不完全清楚代碼中拋出異常的地方,如果你沒有意識到拋出的東西,傳遞調用無法正常清理。 這在拋出異常的庫中尤其邪惡。

我更喜歡以下風格:

retval method(const input&, pointer *output) { }

因為很明顯輸入和輸出如何放在方法上。 這也避免了必須返回像std :: pair那樣無法擴展的時髦結構。

可能最正確的方法是這樣的:

#include <optional>
#include <functional>

std::optional<std::reference_wrapper<const std::string>> Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  if (kitty)
  {
    return kitty->getName();
  }
  return std::nullopt;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM