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

C++ 如何返回不可用的常量引用,c++,c++11,C++,C++11,假设我有以下功能: const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); return kitty->getName(); } 其中kitty::getName返回一个const std::string&如何最好地处理kitty是nullptr的情况?我可以返回std::string(“”),但随后我将返回一个对一个临时的、实际上可以保证未定义行为的引用。我可以更改getKi

假设我有以下功能:

const std::string& Cat::getKittenName() const
{
  Kitten* kitty = getKitty();
  return kitty->getName();
}
其中
kitty::getName
返回一个
const std::string&
如何最好地处理
kitty
nullptr
的情况?我可以返回
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;
}

唯一的问题可能是“魔法静力学”不可用。此解决方案有问题吗?或者有更好的方法吗?

返回对const static std::string的引用

原因:

    《魔法静力学》不是魔术,它们是C++标准的一部分。
  • 静态是在代码第一次流过它们时构造的 (即一次)
  • 从c++11开始,静态构造是线程安全的
  • 静态对象以正确的顺序在 节目结束
  • 一个冗余静态对象的性能损失是完全可以忽略的,并且比测试返回指针是否为null的成本要少得多
如果您在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;
}

你有几个选择,真的

  • 最简单的方法是返回
    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;
    }
    

返回一个指示成功/失败的布尔值,并通过指针传入输出

下面是一个例子:

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
}
这要求指针传入的类具有有效的复制构造函数和复制赋值运算符

我喜欢这样做有几个原因:

  • 返回一个指针是令人困惑的,调用者不清楚该如何处理指针
  • 例如,上面列出的示例:

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

    const std::string *name = mykitty.getKittenName();
    
    o、 现在怎么办?我无法使用此指针更改name的值,但我可以更改指针指向的内容吗?我应该把这个包起来吗

    std::unique_ptr<const std::string>
    
    我不喜欢,因为它鼓励对非异常行为抛出异常。例如,如果您对一组小猫编写了一个名为“find()”的方法。找不到一只叫某个名字的小猫也不例外。STL使用各种机制,如std::end和std::pair,以避免为非异常行为引发异常

    这产生的另一个问题是,与Java不同,代码中抛出异常的位置并不明确,如果您没有意识到抛出异常,则传递调用无法正确清理。这在抛出异常的库中尤其有害

    我更喜欢以下风格:

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

    因为很清楚输入和输出是如何放置在方法上的。这也避免了必须返回像std::pair这样不可伸缩的时髦构造。

    最干净的解决方案是,在知道“冗余”拷贝非常重要之前,不要担心它们。是否引发异常?引用的预期属性是它们从不为null。@AlexFarber有一个副本,而返回引用时没有副本。此外,语义也不同。所以简单地按值返回可能不是正确的事情。@ MulbDNILO这个特定的例子可能不会使我的应用程序本身慢下来,但是字符串复制是C++中的一个真正的性能问题。如果我不太关心性能,那为什么还要使用C++?如果字符串大小很小(20字符左右),你可能会惊讶地发现,拷贝通常比引用快或快。选择c++(11)的原因不是性能,而是安全。首先编写安全代码,如果速度太慢(不会太慢),则加快编写速度。不要试图快速编写代码-你会得到不总是有效的代码。你能详细说明一下这与OP的代码有什么不同,以及它是如何处理OP引用的魔法静态不可用的问题的吗?我不确定这是一个什么改进。事实上,情况似乎更糟,因为非名称是构造的,即使它从来都不需要,也要分配冗余堆内存。a)它是常量,b)静态不是魔法-它们是一种可以预期的语言特性。@RichardHodges“magic statics”指的是它们的线程安全性,并不是所有的编译器都能实现它。我认为(相当强烈地)返回指针是非常邪恶的。返回可选名称更好,但现在需要调用方区分缺少的名称和空白名称。这使得他的谓词更加复杂。返回空的const static字符串(引用)无疑是最安全的方法,在调用站点上出现错误的机会最少。@RichardHodges我也不会沿着指针路线走,但出于完整性考虑,我想提及它。OP最了解他们的需求,并能决定最适合他们的。也许C程序员还是这样
    const std::string& Cat::getKittenName() const
    {
      Kitten* kitty = getKitty();
      if (kitty)
        return kitty->getName();
      else
        throw std::invalid_argument("Missing kitten");
    }
    
    retval method(const input&, pointer *output) { }