如何返回不可用的const引用

时间:2015-05-06 09:15:40

标签: c++ c++11

说我有以下功能:

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;
}

唯一的问题可能是魔法静电'不可用。这个解决方案有什么问题,或者有更好的方法吗?

3 个答案:

答案 0 :(得分:25)

你有几个选择,真的。

  • 最简单的一个是返回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");
    }
    

答案 1 :(得分:5)

返回对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;
}

答案 2 :(得分:0)

返回表示成功/失败的布尔值,并通过指针传递输出。

以下是一个例子:

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. 返回指针令人困惑,并且调用者不清楚如何处理指针。
  2. 例如上面列出的例子:

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

    会像这样调用:

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

    o.k ...现在怎样?我无法使用此指针更改name的值,但是我可以更改指针指向的内容吗?我应该将其包装在

    std::unique_ptr<const std::string>
    

    ??一般来说,返回指针非常C-esque,除了令人困惑之外,还是内存泄漏和错误的重要来源。

    1. 抛出异常
    2. 这个例子:

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

      我不是粉丝,因为它鼓励为非例外行为抛出异常。例如,如果您编写了一个名为&#39; find()&#39;的方法。在一套小猫。它不是一个例外&#39;找不到特定名字的小猫。 STL使用各种机制,如std :: end和std :: pair,以避免为非异常行为抛出异常。

      这产生的另一个问题是,与Java不同,它并不完全清楚代码中抛出异常的地方,如果您没有意识到某些东西会抛出,则传递调用无法正常清理。在抛出异常的库中,这尤其邪恶。

      我更喜欢以下的风格:

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

      因为它清楚输入和输出如何放在方法上。这也避免了必须返回像std :: pair那样无法扩展的时髦结构。