【问题标题】:C++ std::string and NULL const char*C++ std::string 和 NULL const char*
【发布时间】:2009-11-12 17:59:40
【问题描述】:

我正在使用 C++ 编写两段大代码,一段以“C 风格”完成,另一段以“C++ 风格”完成。

C 类型代码具有返回 const char* 的函数,而 C++ 代码在很多地方都有类似

const char* somecstylefunction();
...
std::string imacppstring = somecstylefunction();

它从 C 样式代码返回的 const char* 构造字符串。

这一直有效,直到 C 样式代码更改并开始有时返回 NULL 指针。这当然会导致段错误。

周围有很多代码,所以我想以最简洁的方式解决这个问题。在这种情况下,预期的行为是 imacppstring 将是空字符串。有没有一个很好的、巧妙的解决方案?

更新

这些函数返回的 const char* 总是指向静态字符串的指针。它们主要用于传递有关函数中任何意外行为的信息性消息(最有可能用于记录日志)。决定让这些在“无报告”上返回 NULL 很好,因为这样您就可以将返回值用作条件,即

if (somecstylefunction()) do_something;

而在函数返回静态字符串“”之前;

这是否是一个好主意,我不会接触这段代码,而且这也不取决于我。

我想要避免的是跟踪每个字符串初始化以添加包装函数。

【问题讨论】:

  • 我要提一下,正如您在评论中提到的那样,您是“维护者”,可能违反了一些非常重要的样式规则。我不知道您为什么要返回指向 char 的指针,除非它通过指针传递到函数中。你能详细说明一下吗?你可能有更大的问题。
  • 谁能回答为什么 std::basic_string 实现不将 null value_type 指针视为空字符串?
  • @jmucchiello,我认为因为从根本上说,“空字符串”与指向内存地址 0 的指针非常不同。空字符串 "" 实际上占用内存作为单个空字符。
  • 您是否尝试过与决定开始返回空指针的人交谈,并解释它破坏了 C++ 代码?
  • 如果函数返回“”,应该仍然可以进行简单的检查:if (*somecstylefunction())

标签: c++ string null


【解决方案1】:

最好的办法可能是将 C 库函数修复到其破坏前的更改行为。但也许您无法控制该库。

要考虑的第二件事是更改所有依赖于 C lib 函数返回空字符串的实例,以使用将“修复”NULL 指针的包装函数:

const char* nullToEmpty( char const* s)
{
    return (s ? s : "");
}

那么现在

std::string imacppstring = somecstylefunction();

可能看起来像:

std::string imacppstring( nullToEmpty( somecstylefunction());

如果这是不可接受的(这可能是很多忙碌的工作,但它应该是一次性的机械更改),您可以实现一个与您当前使用的 C 库同名的“并行”库,这些函数只需调用原始 C lib 函数并根据需要修复 NULL 指针。您需要使用标头、链接器和/或 C++ 命名空间来玩一些棘手的游戏才能使其正常工作,这很有可能在未来造成混乱,所以在走这条路之前我会三思而后行.

但以下内容可能会让您入门:

// .h file for a C++ wrapper for the C Lib
namespace clib_fixer {
    const char* somecstylefunction();
}


// .cpp file for a C++ wrapper for the C Lib
namespace clib_fixer {
    const char* somecstylefunction() {
        const char* p = ::somecstylefunction();

        return (p ? p : "");
    }
}

现在您只需将该标头添加到当前调用 C lib 函数的 .cpp 文件中(并且可能删除 C lib 的标头)并添加一个

using namespace clib_fixer;

使用这些函数到 .cpp 文件。

这可能不是糟糕。也许吧。

【讨论】:

  • "并且可能删除 C lib 的标头" - 我很确定您必须删除它。我认为否则对somecstylefunction 的调用将在namespace clib_fixernamespace :: 之间产生歧义。
【解决方案2】:

好吧,在不更改直接从 C 函数调用初始化 C++ std::string 的每个位置(以添加空指针检查)的情况下,唯一的解决方案是禁止 C 函数返回空指针。

在 GCC 编译器中,您可以使用编译器扩展“带有省略操作数的条件”来为您的 C 函数创建包装宏

#define somecstylefunction() (somecstylefunction() ? : "")

但在一般情况下,我建议不要这样做。

【讨论】:

  • 是的,这似乎是我想要避开的事情。解决问题的根源可能比解决症状更好。
  • 某种形式的宏或一组您自己的库包装器将是解决问题的好方法,而无需更改原始库。
  • @antonmarkov:是的,但是在我看来宏诡计编译器扩展的组合有点过分了。
【解决方案3】:

我想你可以添加一个包装函数来测试 NULL,并返回一个空的 std::string。但更重要的是,为什么你的 C 函数现在返回 NULL? NULL 指针表示什么?如果它指示严重错误,您可能希望包装函数抛出异常。

或者为了安全起见,你可以只检查 NULL,处理 NULL 的情况,然后才构造一个 std::string。

const char* s = somecstylefunction();
if (!s) explode();
std::string str(s);

【讨论】:

  • 在这种情况下,它并不表示错误。维护者决定用空指针而不是指向静态“”的指针来指示“无消息”更简洁。这不是不合理的,但会破坏其他代码。
  • 在这种情况下,您可以简单地使用一个内联包装函数来测试 NULL,如果指针为 NULL,则返回一个空的 std::string。
【解决方案4】:

对于便携式解决方案:

(a) 定义您自己的字符串类型。最大的部分是对整个项目的搜索和替换——如果它总是 std::string 或一次性的痛苦,这可能很简单。 (我唯一的要求是它可以 Liskov 替代 std::string,但也可以从 null char * 构造一个空字符串。

最简单的实现是从 std::string 公开继承。即使这是不赞成的(出于可以理解的原因),在这种情况下也可以,并且还可以帮助期望 std::string 的 3rd 方库以及调试工具。或者,聚合和转发 - 呸。

(b) #define std::string 为您自己的字符串类型。有风险,不推荐。除非我非常了解所涉及的代码库并为您节省大量工作,否则我不会这样做(我会添加一些免责声明以保护我的声誉;))

(c) 我已经解决了一些这样的情况,方法是将攻击性类型重新#define'ing 到某些实用程序类,仅用于包含的目的(因此#define 的范围更加有限)。但是,我不知道如何为char * 做到这一点。

(d) 编写一个导入包装器。如果 C 库标头具有相当规则的布局,并且/或者您认识具有解析 C++ 代码经验的人,您可能能够生成“包装标头”。

(e) 要求库所有者至少在编译时使“空字符串”值可配置。 (一个可接受的请求,因为切换到 0 在其他情况下也会破坏兼容性)如果这对您来说工作量较小,您甚至可以提出自己提交更改!

【讨论】:

    【解决方案5】:

    您可以将所有对 C-stlye 函数的调用包装在这样的东西中......

    std::string makeCppString(const char* cStr)
    {
        return cStr ? std::string(cStr) : std::string("");
    }
    

    那么无论你有什么:

    std::string imacppstring = somecstylefunction(); 
    

    替换为:

    std::string imacppstring = makeCppString( somecystylefunction() );
    

    当然,这假设当您的函数返回 NULL 时构造一个空字符串是可接受的行为。

    【讨论】:

    • std::string imacppstring = makeCppString(somecystylefunction); 将不起作用,因为您传递的是函数的地址,而不是返回的 char const *
    【解决方案6】:

    我一般不提倡对标准容器进行子类化,但在这种情况下它可能会起作用。

    class mystring : public std::string
    {
        // ... appropriate constructors are an exercise left to the reader
        mystring & operator=(const char * right)
        {
            if (right == NULL)
            {
                clear();
            }
            else
            {
                std::string::operator=(right);  // I think this works, didn't check it...
            }
            return *this;
        }
    };
    

    【讨论】:

    • @Walter,我从来没有说过这是一个的想法,只是一种利用糟糕情况的方法。它有效:ideone.com/2xGd1A
    • 在 C++11 中,为了让移动语义在 std::stringmystring(任一方向)之间起作用,我需要重载哪些其他成员(除了构造函数)?我不需要将右值转换为std::string&& 吗?
    【解决方案7】:

    这样的事情应该可以解决您的问题。

    const char *cString;
    std::string imacppstring;
    
    cString = somecstylefunction();
    if (cString == NULL) {
      imacppstring = "";
    } else {
      imacppstring = cString;
    }
    

    如果您愿意,您可以将错误检查逻辑粘贴到它自己的函数中。那么你必须把这个代码块放在更少的地方。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      相关资源
      最近更新 更多