代码之家  ›  专栏  ›  技术社区  ›  florin

在一个集合中保存一堆常量字符*的最简单、最安全的方法?

  •  4
  • florin  · 技术社区  · 17 年前

    我想将一组const char指针保存到std::set容器[1]中。STD::SET模板需要一个比较器函数,标准C++库提供STD::更少,但是它的实现是基于直接比较两个键,这对指针来说不是标准的。

    我知道我可以定义自己的functor并通过将指针转换为整数并进行比较来实现操作符(),但是有没有更干净的“标准”方法呢?

    请不要建议创建std::strings-这是浪费时间和空间。字符串是静态的,因此可以根据它们的地址比较它们是否相等。

    1:指针指向静态字符串,因此它们的生命周期没有问题——它们不会消失。

    8 回复  |  直到 17 年前
        1
  •  8
  •   Ruben Verborgh    11 年前

    如果你不想把它们包起来 std::string s、 可以定义函子类:

    struct ConstCharStarComparator
    {
      bool operator()(const char *s1, const char *s2) const
      {
        return strcmp(s1, s2) < 0;
      }
    };
    
    typedef std::set<const char *, ConstCharStarComparator> stringset_t;
    stringset_t myStringSet;
        2
  •  3
  •   paercebal    17 年前

    “优化方式”

    如果我们忽略了“过早优化是万恶之源”,标准的方法是添加一个比较器,这很容易编写:

    struct MyCharComparator
    {
       bool operator()(const char * A, const char * B) const
       {
          return (strcmp(A, B) < 0) ;
       }
    } ;
    

    要与以下内容一起使用:

    std::set<const char *, MyCharComparator>
    

    标准方式

    使用:

    std::set<std::string>
    

    即使将静态const char*放入其中,它也会工作(因为std::string与const char*不同,它的内容是可比较的)。

    当然,如果需要提取数据,则必须通过std::string.c_str()提取数据。另一方面,由于它是一个集合,我猜您只想知道“AAA”是否在集合中,而不是提取“AAA”的值“AAA”。

    注意:我确实读过关于“请不要建议创建std::strings”的文章,但随后,您询问了“标准”方式。。。

    “永远不要做”的方式

    字符串是静态的 (在)基于地址的平等 .

    这有点C的味道(使用了不推荐使用的“static”关键字,可能用于std::string bashing的过早优化,以及通过地址进行字符串比较)。

    无论如何 您不想通过字符串的地址来比较它们。

    { "AAA", "AAA", "AAA" }
    

    当然,如果只使用相同的全局变量来包含字符串,则情况就不同了。

    在这种情况下,我建议:

    std::set<const char *>
    

    当然,这对我来说是行不通的 静态常量字符*

    但这是另一个故事。

        3
  •  3
  •   fizzer    17 年前

    只需继续使用默认顺序,即更少<>。该标准保证less即使对指向不同对象的指针也有效:

    “对于模板大、小、大、小,任何 指针类型产生总顺序,即使内置运算符<><=>=不要。”

    set<const char*> .

        4
  •  0
  •   Greg Hewgill    17 年前

    std::string 她在片场里。这样,您就不必编写任何额外的粘合代码。

        5
  •  0
  •   Matt Dillard    17 年前

    这个集合必须包含 const char* ?

    立即浮现在脑海中的是将字符串存储在 std::string 取而代之的是,把它们放到 std::set . 这将允许比较没有问题,并且您始终可以获得原始数据 常量字符* 通过简单的函数调用:

    const char* data = theString.c_str();
    
        6
  •  0
  •   xtofl Adam Rosenfield    17 年前

    可以使用比较器,也可以使用要包含在集合中的包装器类型。(注: std::string

    const char* a("a");
    const char* b("b");
    
    struct CWrap {
        const char* p;
        bool operator<(const CWrap& other) const{
            return strcmp( p, other.p ) < 0;
        }
        CWrap( const char* p ): p(p){}
    };
    
    std::set<CWrap> myset;
    myset.insert(a);
    myset.insert(b);
    
        7
  •  0
  •   Community Mohan Dere    6 年前

    其他人已经发布了大量的解决方案,展示了如何与 const char* ,这样我就不用麻烦了。

    请不要建议创建std::strings-这是浪费时间和空间。

    如果 std::string std::set 也可能是浪费时间和空间。表中的每个元素 std::set 与免费商店分开分配。根据您的程序使用集合的方式,这可能对性能的影响大于 std::set std::vector

    标准C++库提供STD::更少,但是它的实现是基于直接比较两个键,这对于指针来说是不标准的。

    这取决于指针指向什么。如果所有密钥都是从同一数组分配的,则使用 operator< 比较指针不是未定义的行为。

    包含单独静态字符串的数组示例:

    static const char keys[] = "apple\0banana\0cantaloupe";
    

    std::set<const char*> 并用指向该数组的指针填充它,它们的顺序将得到很好的定义。

    如果您的编译器/链接器支持并启用了字符串池,则重复的字符串文本应该具有相同的地址,但是否保证在所有情况下都具有相同的地址?依靠链接器优化获得正确的功能是否安全?

    也许 可以基于字符串第一次使用的顺序,但如果将另一个翻译单元更改为使用相同的字符串文字之一,则集合顺序可能会更改。

    指向 uintptr_t 使用指针比较似乎没有任何好处。无论哪种方式,结果都是一样的:特定于实现。

        8
  •  -1
  •   Andrew Top    17 年前

    由于性能原因,您可能不想使用std::string。

    我在运行MSVC和gcc,他们似乎都不介意:

    bool foo = "blah" < "grar";
    

    编辑:但是,本例中的行为未指定。请参阅评论。。。

    他们也不会抱怨 std::set<const char*> .

    如果您使用的编译器确实有问题,我可能会继续使用您建议的将指针强制转换为的函子 int s

    编辑: 嘿,我被否决了。。。尽管是这里少数几个最直接回答他的问题的人之一。我不熟悉堆栈溢出,如果发生这种情况,有什么办法保护自己吗?话虽如此,我将尝试在这里:

    std::string 解决。每次您输入 字符串 在集合中,它需要复制整个字符串(直到C++0x成为标准)。此外,每次执行集合查找时,都需要执行多个字符串比较。

    但是,将指针存储在集合中不会产生字符串复制(只是复制指针),并且每次比较都是地址上的简单整数比较,而不是字符串比较。

    const char* 超过 或调用 strcmp . 是的,它不太安全,更容易出错,但这些都是性能的常见权衡,而且由于问题从未说明应用程序,我认为我们应该假设他已经考虑了利弊,并决定支持性能。