代码之家  ›  专栏  ›  技术社区  ›  activout.se

可以为NULL的C++字符串

  •  7
  • activout.se  · 技术社区  · 16 年前

    我习惯在C++应用程序中传递这样的字符串:

    void foo(const std::string& input)
    {
      std::cout << input.size() << std::endl;
    }
    
    void bar()
    {
      foo("stackoverflow");
    }
    

    现在我有一个例子,我希望字符串为NULL:

    void baz()
    {
      foo("stackoverflow");
      foo(NULL); // very bad with foo implementation above
    }
    

    我可以改变 foo 致:

    void foo(const std::string* input)
    {
      // TODO: support NULL input
      std::cout << input->size() << std::endl;
    }
    

    但要传递字符串文字或复制 char* 实施 足球 我需要写这样的东西:

    void bar()
    {
      string input("hi"); // annoying temporary
      foo(&input);
      foo(NULL);  // will work as long as foo handles NULL properly
    }
    

    我开始考虑继承 std::string 并添加a null 房地产,但我不太确定这是个好主意。也许简单地使用一个 const char* string用于可以为NULL的参数,但如果我想保存字符串(或NULL)的副本,而不必自己管理其内存,该怎么办?(参见 What are some of the drawbacks to using C-style strings? 等等)

    有什么巧妙的解决方案吗?

    7 回复  |  直到 8 年前
        1
  •  20
  •   Stack Overflow is garbage    16 年前

    如果你想让类型为null,那么就把它变成一个指针。传递字符串指针而不是引用,因为这正是指针可以做的,而引用不能做的。引用总是指向同一个有效对象。指针可以设置为null,也可以重新设置为指向另一个对象。因此,如果你需要指针可以做的事情,就使用指针。

    或者,使用boost::optional,它允许一种更类型安全的方式指定“此变量可能包含也可能不包含值”。

    或者,当然,更改语义,这样你就可以使用空字符串而不是null,传递一个单独的bool参数来指定字符串是否可用,或者重构,这样你一开始就不需要这个。

        2
  •  11
  •   eplawless    16 年前

    功能超载救援。..

    void foo( const std::string& input )
    {
        std::cout << input << std::endl;
    
        // do more things ...
    }
    
    void foo( const char* input )
    {
        if ( input != NULL ) foo( std::string(input) );
    }
    

    这将同时接受c样式的char数组和std::string,如果你传入一个字符串文字或char数组,将在堆栈上产生额外的开销,但允许你将实现保持在一个地方并保持良好的语法。

        3
  •  10
  •   Max Lybbert    16 年前

    就我个人而言,我会改变语义,传递空的std::string而不是NULL:

    void foo(const std::string& input)
    {
        if (!input.empty())
            std::cout << input.size() << std::endl;
    }
    
    void bar()
    {
          foo("");
    }
    
        4
  •  3
  •   Matt McClellan    16 年前

    或者,将前面的两个答案混合在一起:

    void fooImpl( const char* input )
    {
        if ( input != NULL )
            std::cout << input << std::endl;
    }
    
    void foo( const std::string& input )
    {
        fooImpl(input.c_str());    
    }
    
    void foo( const char* input )
    {
        fooImpl(input);
    }
    

    相同的接口,堆栈上没有副本。如果你愿意,你也可以使用内联fooImpl。

        5
  •  2
  •   Konrad Rudolph    16 年前

    为什么不重载该函数,并且不给第二个重载任何参数?然后,这两个重载都可以在内部调用一个提供读取逻辑的函数,该函数本身会传递一个指针给 std::string .

    void foo_impl(string const* pstr) { … }
    
    void foo(string const& str) {
        foo_impl(&str);
    }
    
    void foo() {
        foo_impl(0);
    }
    
        6
  •  2
  •   Tom    16 年前

    绝对不能继承自 std::string 继承是C++中最紧密的耦合,你只是在寻找可空性,你可以简单地用 const char* 、过载或简单 std::string * 如果你真的想。

        7
  •  1
  •   Greg Hewgill    16 年前

    如果你只是使用:

    void foo(const char *xinput)
    {
        if (xinput == NULL) {
            // do something exceptional with this
            return;
        }
        std::string input(xinput);
        // remainder of code as usual
    }
    

    是的,这确实会产生额外的分配和复制,调用该函数有点冗长,因为您需要使用 .c_str() 在通常情况下,但它确实给了你想要的语义。