【问题标题】:How to write a function returning const char*, which is a modified const char* argument?如何编写一个返回 const char* 的函数,它是一个修改后的 const char* 参数?
【发布时间】:2012-02-27 19:58:17
【问题描述】:

我要写一个函数(c++),定义是:

const char* getFileName(const char* name);

为了简单起见,这个函数会取名字,检查一些条件并附加一个后缀,使它成为一个正确的文件名。例如:

name = "someFile", return = "someFile-hd.png"

此函数返回的const char* 将立即传递给另一个,它以const char* 作为参数

something->loadFile(getFileName("someFile"));

我遇到的问题是,当我在我的函数中创建一个 const char* 时,它的范围将限于创建它的函数。另一方面,我无法修改 loadFile 的代码函数,因为它是我正在使用的第 3 方库。

我可以在loadFile 周围编写一个包装器来删除我创建的const char*,但我不想这样做,因为这是一个移植练习,这会破坏我创建的一些模板更容易。

有没有办法像我描述的那样编写这个函数?

谢谢

编辑: 小解释:

getFileName 是我正在尝试编写的函数,我可以更改返回类型,但它必须适合作为 loadFile 函数的参数。我的主要问题是getFileName 返回的const char* 要么因为超出范围而丢失,要么我必须明确删除它,这会破坏我的移植模板。我想在一个函数调用中生成文件名,对此不做任何事情[换句话说,在原始代码中我有loadFile("something.png"),我想将其更改为loadFile(getFileName("something")),之后不添加任何新行。

【问题讨论】:

  • getFileName() 是否是线程安全的/可重入的?如果没有,您可以返回指向该函数本地的 static 缓冲区的指针。
  • 是的,我正在使用 pthreads,并且希望它是线程安全的,只是为了确定。
  • 可以改一下getFileName()的返回类型吗?
  • 澄清一下,您的主要问题是删除 getFileName 返回的 const char* 吗?
  • @FrédéricHamidi 即使不需要重入,返回静态也是一个非常糟糕的主意。考虑something->loadSeveralFiles( getFileName("someFile"), getFileName("SomeOtherFile") );。糟糕,它两次打开同一个文件并忽略了另一个。 不要返回指向静态的指针,将此变量的分配留给调用者。或者更好的是,如果可以选择使用 std::string。

标签: c++ c scope


【解决方案1】:

使用const 参数的主要逻辑是,它意味着您不会在接受它的函数体中更改此参数。

如果要更改,请不要使用const

不用担心将char* 转换为const char*。对此有隐式转换。

不过,您实际上并不需要更改此 const char* 参数。您可以使用它来构造std::string 对象并避免可能的内存泄漏 - 检查hmjd 的答案。

【讨论】:

  • 这虽然是内存泄漏...并且效率稍低,因为strcat 将重新计算第一个字符串的长度,而strcpy 已经完成(尽管这是 C API 错误,而不是你的)。
  • 这取决于他打算如何使用它。如果他要打电话给something->loadFile(getFileName("someFile")); 那么它不会泄漏还是会泄漏?为了提高效率,我认为它仍然比std::string的构造和hjmd复制解决方案的开销要快。
  • @LihO:这取决于 loadFile 的作用。由于 loadFile 是 "something" 的成员,但 getFileName 不是,假设 loadFile 将清理不属于其自己类的内存可能很奇怪。我同意马蒂厄的观点,这会泄漏内存,因此是不好的做法。此外,在 C++ 中返回这样的指针确实没有意义。
  • 是的。现在我意识到,hmjd 的解决方案要好得多。谢谢你们。我已经编辑了我的答案。
【解决方案2】:

从函数返回指针在 C 中通常没有意义(在 C++ 中更是如此)。 由于您同时标记了 C 和 C++,这就是 C 的答案。

执行此操作的常见方法如下:

void getFileName (const char* name, 
                  char*       complete_name, 
                  size_t      complete_name_n)
{
  ... // create a new file name in a temp buffer

  if(the new file name has a strlen() < complete_name_n)
  {
    strcpy(complete_name, the new file name);
  }
}

// caller:
char name_buf [N];

getFileName(name, name_buf, N);
something->LoadFile(name_buf);

例如,整个 Windows API 完全以这种方式调用函数,将参数分配留给调用者,让函数只关心其任务(而不关心内存分配等)。

当然,这意味着调用者必须键入多行而不是一行。此时你应该问问自己什么是最重要的:

  • 编写干净的函数接口,没有晦涩的指针返回,也没有奇怪的、不必要的、泄漏的动态分配,
  • 在调用程序中输入尽可能少的代码,以减少键盘和程序员的磨损。

【讨论】:

    【解决方案3】:

    如果您可以将getFileName() 的返回类型更改为std::string,您可以执行以下操作:

    std::string getFileName(const char* a_name)
    {
        std::string result(a_name);
        result += ".png";
    
        return result;
    }
    
    // No memory management req'd
    something->loadFile(getFileName("someFile").c_str());
    

    【讨论】:

    • 该死 :) 我考虑过使用字符串,但我返回了 result.c_str() 而不是只返回字符串并在之后执行 .c_str() !非常感谢!这在不破坏我的模板的情况下解决了它!
    • 据我所知,为此目的使用.c_str() 被认为是不好的做法,因为您不能依赖std::string result 的副本的存在。
    • @Liho,谢谢,但我认为你可以,因为临时对象会一直存在到表达式结束。
    • @Liho:这可行并且符合标准,尽管它很脆弱。标准明确保证任何临时文件将一直存在到完整表达式的末尾(即,直到到达;),因此getFileName 的结果将一直存在到loadFile 已执行,但不再执行。另一方面,如果有人将其重构为两行char const* name = getFileName("someFile").c_str(); s-&gt;loadFile(name);,那么这将具有未定义的行为 --> 易碎,易碎......
    • @MatthieuM.:谢谢,我明白了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-23
    • 2011-12-26
    • 2019-01-31
    • 2013-11-25
    • 2012-05-26
    • 1970-01-01
    • 2021-11-17
    相关资源
    最近更新 更多