C++ 返回std::string的函数返回零

C++ 返回std::string的函数返回零,c++,C++,在维护大型遗留代码库时,我遇到了这个函数,它充当XML树的访问器 std::string DvCfgNode::getStringValue() const { xmlChar *val = xmlNodeGetContent(mpNode); if (val == 0) return 0; std::string value = (const char *)val; xmlFree(val); return value; } 此函数如

在维护大型遗留代码库时,我遇到了这个函数,它充当XML树的访问器

std::string DvCfgNode::getStringValue() const
{
    xmlChar *val = xmlNodeGetContent(mpNode);
    if (val == 0)
        return 0;

    std::string value = (const char *)val;
    xmlFree(val);
    return value;
}
此函数如何返回“0”?在C中,可以返回零指针作为char *,指示没有找到数据,但是C++中是否可以使用STD::string?我不这么认为,但不太懂C++。

10年的代码编译并运行在C++ 98下。 编辑一

谢谢大家的评论。回答几个问题:

一个代码实际上已经有18年的历史了,大约有500K-1M行

异常处理是打开的,但除了main中的一些块外,任何地方都没有try-catch块,这会导致程序立即终止

c调用代码的设计很差,它似乎信任getStringValue返回正确的值,因此有很多类似于:

std::string s = pTheNode->getStringValue()

可能只是幸运的是,它从未返回零,或者如果它返回了零,那么直到现在还没有人发现这个bug。

这取决于您想要如何发出没有数据的信号。如果没有数据意味着xml树中存在空字符串值,则只需返回空字符串即可

如果您想建模,例如,树中没有数据项,因此树中没有数据,那么根据您的数据语义,您有几个选项

如果数据是强制性的,并且应该存在,那么您有一个具有违犯不变量的对象,即处于非法状态的对象。使用该对象做任何事情都是非法的。我会终止程序,或者使用其他合适的终止机制,例如错误报告器,或者抛出保证不会被捕获和处理的东西。 如果数据是可选的,则可以返回对此进行建模的内容。在C中,您可能会使用指向对象的指针,该对象可以为null,但这会带来所有权问题。在C++中,您可以返回一个STD::可选的,它正好描述了这个。
这取决于您想要如何发出没有数据的信号。如果没有数据意味着xml树中存在空字符串值,则只需返回空字符串即可

如果您想建模,例如,树中没有数据项,因此树中没有数据,那么根据您的数据语义,您有几个选项

如果数据是强制性的,并且应该存在,那么您有一个具有违犯不变量的对象,即处于非法状态的对象。使用该对象做任何事情都是非法的。我会终止程序,或者使用其他合适的终止机制,例如错误报告器,或者抛出保证不会被捕获和处理的东西。 如果数据是可选的,则可以返回对此进行建模的内容。在C中,您可能会使用指向对象的指针,该对象可以为null,但这会带来所有权问题。在C++中,您可以返回一个STD::可选的,它正好描述了这个。
您关于零指针为char*的直觉是正确的。发生的情况是0被解释为空指针,导致返回的字符串从const char*null指针初始化

不过,这是C++中的未定义行为。std::stringconst char*构造函数需要指向以null结尾的字符串的指针。所以你发现了一个bug。修复实际上取决于函数的需求,但我认为抛出异常将是对未定义行为的改进*


*这是一种严重的轻描淡写。代码不应该有未定义的行为

您关于零指针为char*的直觉是正确的。发生的情况是0被解释为空指针,导致返回的字符串从const char*null指针初始化

不过,这是C++中的未定义行为。std::stringconst char*构造函数需要指向以null结尾的字符串的指针。所以你发现了一个bug。修复实际上取决于函数的需求,但我认为抛出异常将是对未定义行为的改进*


*这是一种严重的轻描淡写。代码不应该有未定义的行为

如果代码库是旧的,很可能是它没有真正抛出异常。可能它甚至在编译时关闭了异常处理。。因此,基本上,函数的签名表明,总会有结果,而实现表明,这是不正确的。我看到的最简单的修复方法是将返回值从0更改为返回std::string。这至少会使代码更具可读性。@BitTicker代码具有未定义的行为。这就是需要解决的问题。解决方法可能是抛出异常。另一种可能是返回空字符串。没有足够的信息来知道哪一个是最好的,但我不想返回空字符串,因为它是一个有效字符串,这意味着客户端代码必须检查空字符串,以确定函数是否存在错误条件。同意-建议检查调用代码在此处的期望值。如果有

hing通常是未定义的行为,它通常在特定情况下仍会执行特定的操作。返回一个空字符串是一个很好的近似值,除非这个函数的调用者被try弄得不知所措。。捕获块,因为它们期望出现异常。@BitTicker关于UB错误,关于空字符串错误,关于调用方的try-catch块错误。BitTicker,好建议,谢谢。我修改了它以返回一个空字符串,这是调用ode的逻辑可以接受的。如果代码基是旧的,很可能它并没有真正抛出异常。可能它甚至在编译时关闭了异常处理。。因此,基本上,函数的签名表明,总会有结果,而实现表明,这是不正确的。我看到的最简单的修复方法是将返回值从0更改为返回std::string。这至少会使代码更具可读性。@BitTicker代码具有未定义的行为。这就是需要解决的问题。解决方法可能是抛出异常。另一种可能是返回空字符串。没有足够的信息来知道哪一个是最好的,但我不想返回空字符串,因为它是一个有效字符串,这意味着客户端代码必须检查空字符串,以确定函数是否存在错误条件。同意-建议检查调用代码在此处的期望值。如果某件事在一般情况下是未定义的行为,它通常在特定情况下仍会执行特定的操作。返回一个空字符串是一个很好的近似值,除非这个函数的调用者被try弄得不知所措。。捕获块,因为它们期望出现异常。@BitTicker关于UB错误,关于空字符串错误,关于调用方的try-catch块错误。BitTicker,好建议,谢谢。我修改了它以返回一个空字符串,这是调用ode的逻辑可以接受的。他们必须在修复过于激进(可能导致应用程序完全重写)和过于胆小之间做出微妙的选择。我可能会找到所有的呼叫者,根据呼叫者的数量和他们目前的期望,我会在std::optional(如果呼叫者数量很小)和空字符串(如果呼叫者数量很大)之间做出选择。抛出异常很可能需要更改调用方代码以保持应用程序运行。@BitTickler我不同意。我会尝试定义函数的语义,并根据这一点,例如,有效对象是否总是有值?决定错误修复。从维护的角度来看,正如juanchopanza在他的回答中指出的,当前代码包含未定义的行为,这意味着在选择修复时有很大的自由度,因为在“val==0”的情况下,当前没有人可以依赖该行为。没有一个客户可以被打破,因为没有一个客户可以做出任何假设。这是一个有着未定义行为的棘手问题。代码存在,调用此函数的代码也存在。因此,他们做出了一个假设。调用方站点的实现就是基于这个假设。当然,现在,随着标准库的更新,如果标准库的新版本执行一些不同的未定义行为,那么这些假设可能会变得错误。但是,例如,如果当前的代码库由于返回0而没有遇到异常,那么我认为现在抛出一个这样的想法是不好的,因此肯定会破坏调用代码。@ BitTickler仍然不相信。所讨论的代码在路径val==0上包含UB。如果这条路从未走过,我可以改变它,因为它不会打断呼叫者。如果执行路径,程序将执行UB。我认为这是一个必须修复的bug,即使程序不知怎的通过了测试,因为测试没有验证任何东西。参数表示代码存在,调用函数也存在。因此,他们所做的假设是不正确的,因为您无法对UB进行假设。@BitTicker另外,您正在更改代码并重新编译它,因此以前测试过的内容现在可能与UB不同。其他编译器版本,其他编译器开关,其他任何东西都可以任意更改行为。如果您的代码执行UB,那么您的代码中有错误,应该进行修复。有时我认为编译器供应商最好添加一些随机效果,以防UB被执行,从而防止人们对它做出假设。。。难道没有一个gcc版本启动了Towers of Hanoi吗?代码维护者的角色是保持代码运行。他们必须在修复过于激进(可能导致应用程序完全重写)和过于胆小之间做出微妙的选择。我可能会找到所有的呼叫者,根据呼叫者的数量和他们目前的期望,如果呼叫者的数量
s很小,如果呼叫者数量很大,则为空字符串。抛出异常很可能需要更改调用方代码以保持应用程序运行。@BitTickler我不同意。我会尝试定义函数的语义,并根据这一点,例如,有效对象是否总是有值?决定错误修复。从维护的角度来看,正如juanchopanza在他的回答中指出的,当前代码包含未定义的行为,这意味着在选择修复时有很大的自由度,因为在“val==0”的情况下,当前没有人可以依赖该行为。没有一个客户可以被打破,因为没有一个客户可以做出任何假设。这是一个有着未定义行为的棘手问题。代码存在,调用此函数的代码也存在。因此,他们做出了一个假设。调用方站点的实现就是基于这个假设。当然,现在,随着标准库的更新,如果标准库的新版本执行一些不同的未定义行为,那么这些假设可能会变得错误。但是,例如,如果当前的代码库由于返回0而没有遇到异常,那么我认为现在抛出一个这样的想法是不好的,因此肯定会破坏调用代码。@ BitTickler仍然不相信。所讨论的代码在路径val==0上包含UB。如果这条路从未走过,我可以改变它,因为它不会打断呼叫者。如果执行路径,程序将执行UB。我认为这是一个必须修复的bug,即使程序不知怎的通过了测试,因为测试没有验证任何东西。参数表示代码存在,调用函数也存在。因此,他们所做的假设是不正确的,因为您无法对UB进行假设。@BitTicker另外,您正在更改代码并重新编译它,因此以前测试过的内容现在可能与UB不同。其他编译器版本,其他编译器开关,其他任何东西都可以任意更改行为。如果您的代码执行UB,那么您的代码中有错误,应该进行修复。有时我认为编译器供应商最好添加一些随机效果,以防UB被执行,从而防止人们对它做出假设。。。难道没有一个gcc版本启动了河内塔吗?该函数中的代码还有另一个问题。鉴于xmlNodeGetContentmpNode返回NULL显然有两个原因。一是没有内容。另一个原因是系统内存不足。如果修复函数的语义,例如让它返回std::optional以表示不总是有值,那么仍然需要担心内存不足的情况,这可能需要在这个特定的代码库中使用另一种形式的寻址。该函数中的代码还有另一个问题。鉴于xmlNodeGetContentmpNode返回NULL显然有两个原因。一是没有内容。另一个原因是系统内存不足。如果修复函数的语义,例如让它返回std::optional以表示不总是有值,那么仍然需要担心内存不足的情况,这可能需要在这个特定的代码库中使用另一种形式的寻址。