5

过去我没有太多使用 C++,最近也做了很多 C#,我真的很难再次回到 C++ 的基础知识。这特别棘手,因为工作要求不能使用任何最方便的 C++ 构造,因此所有字符串都必须是 char *,并且没有提供 STL 列表。

我目前正在尝试做的是创建一个字符串列表,使用 STL 或 C# 完全不需要我花时间。基本上我想要一个功能,例如:

char **registeredNames = new char*[numberOfNames];

然后,

RegisterName(const * char const name, const int length)
{
    //loop to see if name already registered snipped
    if(notFound)
    {
        registeredNames[lastIndex++] = name;
    }

}

或者,如果它是 C#...

if(!registeredNames.Contains(name))
{
    registeredNames.Add(name);
}

我意识到它不起作用。我知道传递变量(一个常量指针和一个常量字符串)的常量性质使它相当困难,但我的基本问题是我过去总是通过使用 STL 列表等来避免这种情况,所以我从来没有不得不解决它!

4

14 回答 14

7

有正当理由可以避免 STL。在内存或速度非常重要的固定环境中工作时,有时很难判断 STL 的幕后情况。是的,您可以编写自己的内存分配器,是的,速度通常不是问题,但是跨平台的 STL 实现之间存在差异,这些差异可能很微妙并且可能存在错误。在考虑使用它时,内存可能是我最关心的问题。

记忆是宝贵的,我们如何使用它需要被严格控制。除非你一直走这条路,否则这个概念可能没有意义,但它是真的。我们确实允许在工具中使用 STL(在游戏代码之外),但在实际游戏中是禁止的。另一个相关问题是代码大小。我不太确定 STL 对可执行文件大小的贡献有多大,但我们已经看到使用 STL 时代码大小显着增加。即使您的可执行文件“仅”大了 2M,对于您的游戏的其他内容来说,内存也少了 2M。

STL 确实不错。但它可能会被不知道自己在做什么的程序员滥用。这不是故意的,但是当您不想看到它们时,它可能会带来令人讨厌的惊喜(同样,内存膨胀和性能问题)

我敢肯定你对你的解决方案很满意。

for ( i = 0; i < lastIndex; i++ ) {
    if ( !strcmp(&registeredNames[i], name ) {
        break;    // name was found
    }
}
if ( i == lastIndex ) {
    // name was not found in the registeredNames list
    registeredNames[lastIndex++] = strdup(name);
}

您可能不想使用 strdup。这只是一个示例,说明如何根据您的示例存储名称。您可能希望确保自己不想为新名称分配空间,或者使用您的应用程序中可能已经可用的其他一些内存结构。

请不要写字符串类。我认为字符串类可能是如何不重新设计 C++ 中的基本 C 构造的最糟糕的例子。是的,字符串类可以对你隐藏很多漂亮的细节,但是它的内存使用模式很糟糕,而且这些模式不适合控制台(例如 ps3 或 360 等)环境。大约 8 年前,我们也做过同样的事情。在我们点击主菜单之前分配了 200000+ 内存。内存非常碎片化,我们无法让游戏的其余部分适应固定的环境。我们最终把它撕掉了。

类设计在某些方面非常有用,但这不是其中之一。这是一种观点,但它基于现实世界的经验。

于 2008-09-18T12:17:23.727 回答
6

您可能需要使用 strcmp 来查看字符串是否已存储:

for (int index=0; index<=lastIndex; index++)
{
  if (strcmp(registeredNames[index], name) == 0)
  {
    return; // Already registered
  }
}

然后,如果您确实需要存储字符串的副本,那么您将需要分配一个缓冲区并将字符复制过来。

char* nameCopy = malloc(length+1);
strcpy(nameCopy, name);
registeredNames[lastIndex++] = nameCopy;

您没有提到您的输入是否以 NULL 结尾-如果不是,则需要格外小心,并且 strcmp/strcpy 将不合适。

于 2008-09-18T11:28:57.960 回答
5

如果可移植性是一个问题,您可能需要查看STLport

于 2008-09-18T11:46:39.510 回答
3

为什么不能使用 STL?

无论如何,我建议您实现一个简单的字符串类和您自己的列表模板。这样,您可以使用与通常相同的技术,并将指针和内存管理限制在这些类中。如果你模仿STL,那就更好了。

于 2008-09-18T11:29:27.803 回答
2

如果你真的不能使用 stl(我很遗憾在我从事游戏行业时相信这是真的),那么你能不创建自己的字符串类吗?最基本的字符串类将在构造和赋值时分配内存,并在析构函数中处理删除。稍后您可以根据需要添加更多功能。完全可移植,非常容易编写和单元测试。

于 2008-09-18T11:56:12.827 回答
1

编辑:我想我误解了你的问题。我知道此代码中没有 constness 问题。

我是从头开始做的,但应该是正确的:

static int lastIndex = 0;
static char **registeredNames = new char*[numberOfNames];

void RegisterName(const * char const name)
{
    bool found = false;
    //loop to see if name already registered snipped
    for (int i = 0; i < lastIndex; i++)
    {
        if (strcmp(name, registeredNames[i] == 0))
        {
            found = true;
            break;
        }
    }

    if (!found)
    {
        registeredNames[lastIndex++] = name;
    }
}
于 2008-09-18T11:26:05.567 回答
1

使用 char* 需要您使用 C 函数。在您的情况下,您真正​​需要的是复制字符串。为了帮助你,你有 strndup 功能。然后你必须写一些类似的东西:

void RegisterName(const char* name)
{
  // loop to see if name already registered snipped
  if(notFound)
  {
    registerNames[lastIndex++] = stdndup(name, MAX_STRING_LENGTH);
  }
}

此代码假设您的数组足够大。

当然,最好的办法是正确实现您自己的字符串、数组和列表,……或者让您的老板相信 STL 不再是邪恶的了!

于 2008-09-18T11:29:23.263 回答
1

使用:

const char **registeredNames = new const char * [numberOfNames];

将允许您将 a 分配给const * char const数组的元素。

只是出于好奇,为什么“工作要求不能使用最方便的 C++ 构造”?

于 2008-09-18T11:29:27.540 回答
1

我可以理解为什么你不能使用 STL——大多数确实让你的代码非常臃肿。然而,游戏程序员为游戏程序员提供了一些实现——RDESTL就是这样一个库。

于 2008-09-18T12:13:00.880 回答
0

如果您不担心约定,只想完成工作,请使用 realloc。我一直在为列表做这种事情,它是这样的:

T** list = 0;
unsigned int length = 0;

T* AddItem(T Item)
{
 list = realloc(list, sizeof(T)*(length+1));
 if(!list) return 0;
 list[length] = new T(Item);
 ++length;
 return list[length];
}

void CleanupList()
{
 for(unsigned int i = 0; i < length; ++i)
 {
  delete item[i];
 }
 free(list)
}

您还可以做更多事情,例如每次列表大小翻倍时才重新分配,通过索引或检查相等性从列表中删除项目的功能,制作用于处理列表的模板类等......(我有一个我很久以前写过的,并且总是使用自己......但遗憾的是我在工作,不能在这里复制粘贴)。不过老实说,这可能不会胜过 STL 等价物,尽管如果您做大量工作或 STL 的实现特别差,它可能会与其性能相当。

令人烦恼的是,C++ 没有操作符 renew/resize 来替换 realloc,这将非常有用。

哦,如果我的代码出错了,我很抱歉,我只是把它从内存中拉出来了。

于 2008-09-18T12:37:28.343 回答
0

建议的所有方法都是有效的,我的观点是,如果 C# 的方式很吸引人,请使用提供的示例代码创建自己的类/接口以呈现相同的抽象,即具有方法 Contains 和 Add 的简单链表类通过其他答案,这应该相对简单。

C++ 的一大优点通常是,如果另一种语言有很好的实现,通常可以重现它,你可以让它看起来和按照你想要的方式运行。

于 2008-09-18T13:18:05.670 回答
0

无论您是否使用 STL,const 正确性仍然是 const 正确性。我相信您正在寻找的是制作 registeredNames aconst char **以便对registeredNames[i](即 a const char *)的分配有效。

而且,这真的是你想做的吗?似乎制作字符串的副本可能更合适。

此外,鉴于您正在对其进行的操作,您不应该考虑将其存储在列表中,一组会更好。

于 2008-09-18T13:37:02.147 回答
0

我已经使用这个 String 类多年了。

http://www.robertnz.net/string.htm

它几乎提供了 STL 字符串的所有特性,但被实现为真正的类而不是模板,并且不使用 STL。

于 2008-09-18T23:08:27.843 回答
0

这是一个明显的例子,你可以自己动手。对向量类做同样的事情。

  • 使用测试优先编程来做到这一点。
  • 把事情简单化。

如果您在 MT 环境中,请避免对字符串缓冲区进行引用计数。

于 2008-09-19T17:29:33.520 回答