说我有以下功能:
const std::string& Cat::getKittenName() const
{
Kitten* kitty = getKitty();
return kitty->getName();
}
Kitten::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;
}
唯一的问题可能是魔法静电'不可用。这个解决方案有什么问题,或者有更好的方法吗?
答案 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以来,optional
是std::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的引用。
原因:
如果你在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
}
这要求由指针传入的类具有有效的复制构造函数和复制赋值运算符。
我喜欢这样做有几个原因:
例如上面列出的例子:
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,除了令人困惑之外,还是内存泄漏和错误的重要来源。
这个例子:
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那样无法扩展的时髦结构。