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的成本要少得多
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) { }