在一组中保存一堆const char *的最简单,最安全的方法?

时间:2008-10-24 21:27:41

标签: c++ stl

我想把一堆const char指针放到std :: set容器[1]中。 std :: set模板需要一个比较器函子,标准C ++库提供std :: less,但它的实现基于直接比较两个键,这不是指针的标准。

我知道我可以定义自己的仿函数并通过将指针转换为整数并比较它们来实现operator(),但是有一种更清晰,“标准”的方法吗?

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

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

8 个答案:

答案 0 :(得分:8)

如果您不想将它们包装在std::string中,您可以定义一个仿函数类:

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;

答案 1 :(得分:3)

“优化方式”

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

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”,但是,你问了“标准”的方法......

“永远不要做”的方式

我在回答后注意到以下评论:

  
    

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

  

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

无论如何,你不想通过他们的地址来比较你的字符串。因为我想你想要的最后一件事是有一个包含:

的集合
{ "AAA", "AAA", "AAA" }

当然,如果你只使用相同的全局变量来包含字符串,那么这是另一个故事。

在这种情况下,我建议:

std::set<const char *>

当然,如果比较具有相同内容但不同变量/地址的字符串,它将无效。

当然,如果在标题中定义了这些字符串,它将不能与 static const char * 字符串一起使用。

但这是另一个故事。

答案 2 :(得分:3)

请继续使用默认排序,即少于&lt;&gt ;.标准保证即使对于指向不同对象的指针,less也会起作用:

“对于templates,less,greater_equal和less_equal,any的特化 指针类型产生一个总订单,即使内置运算符&lt;,&gt;,&lt; =,&gt; =也没有。“

保证适用于您set<const char*>之类的内容。

答案 3 :(得分:0)

根据“束”的大小,我倾向于在集合中存储相应的std::string串。这样你就不必编写任何额外的胶水代码了。

答案 4 :(得分:0)

该集必须包含const char*吗?

立即想到的是将字符串存储在std::string中,并将其放入std::set。这样可以毫无问题地进行比较,并且您可以通过简单的函数调用获得原始const char*

const char* data = theString.c_str();

答案 5 :(得分:0)

使用比较器,或使用包装类型包含在集合中。 (注意: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);

答案 6 :(得分:0)

其他人已经发布了大量解决方案,展示了如何与const char*进行词汇比较,所以我不会打扰。

  

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

如果std::string浪费时间和空间,那么std::set也可能浪费时间和空间。 std::set中的每个元素都与免费商店分开分配。根据程序使用集合的方式,这可能会损害性能,而std::set的O(log n)查找有助于提高性能。您可以使用另一个数据结构获得更好的结果,例如排序的std::vector,或者在编译时排序的静态分配的数组,具体取决于集合的预期生命周期。

  

标准C ++库提供了std :: less,但它的实现基于直接比较两个键,这不是指针的标准。

     

字符串是静态的,因此可以根据它们的地址比较(in)相等。

这取决于指针指向的内容。如果所有键都是从同一个数组中分配的,那么使用operator<来比较指针并不是未定义的行为。

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

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

如果你创建一个std::set<const char*>并用指向该数组的指针填充它,它们的排序将是明确定义的。

但是,如果字符串都是单独的字符串文字,则比较它们的地址很可能涉及未定义的行为。它是否有效取决于您的编译器/链接器实现,如何使用它以及您的期望。

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

如果您只在一个翻译单元中使用字符串文字,则集合排序可能基于首次使用字符串的顺序,但如果您更改另一个翻译单元以使用其中一个相同的字符串文字,集合顺序可能会改变。

  

我知道我可以定义自己的仿函数并通过将指针转换为整数并比较它们来实现operator()

将指针转换为uintptr_t似乎没有使用指针比较的好处。结果是相同的两种方式:特定于实现。

答案 7 :(得分:-1)

由于性能原因,大概你不想使用std :: string。

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

bool foo = "blah" < "grar";

编辑:但是,此情况下的行为未指定。见评论......

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

如果您使用的是一个抱怨的编译器,我可能会继续使用您建议的函数来指向int s。

编辑: 嘿,我被拒绝了......尽管是这里为数不多的人中最直接回答他问题的人之一。我是Stack Overflow的新手,如果发生这种情况,有什么办法可以保护自己吗?话虽这么说,我会试着在这里:

问题不是寻找std::string解决方案。每次在集合中输入std::string时,都需要复制整个字符串(无论如何,直到C ++ 0x是标准的)。此外,每次进行设置查找时,都需要进行多次字符串比较。

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

问题表明存储指向字符串的指针很好,我认为没有理由我们都应该立即假设这个语句是错误的。如果您知道自己在做什么,那么使用const char*相对于std::string或调用strcmp的自定义比较,可以获得相当大的性能提升。是的,它不太安全,而且更容易出错,但这些是性能的常见权衡,而且由于问题从未说明过应用程序,我认为我们应该假设他已经考虑了利弊并决定支持性能