如何干净利用:const char *和std :: string?

时间:2015-04-29 10:47:10

标签: c++ string char concatenation

  

TL:博士

     

如何巧妙地将const char*std::string连接起来   优雅地,没有多个函数调用。理想情况下,在一个功能   调用并输出为const char*。这是不可能的,是什么   是最佳解决方案吗?

初始问题

到目前为止,我使用C ++遇到的最大障碍是它如何处理字符串。在我看来,在所有广泛使用的语言中,它处理的字符串最差。我已经看到了类似的其他问题,或者有一个答案说"使用std::string"或者只是指出其中一个选项最适合您的情况。

然而,当尝试动态使用字符串时,这是无用的建议,就像在其他语言中使用它们一样。我无法保证始终能够使用std::string,并且在我必须使用const char*的时候,我会触及明显的“#34;它不变”,你可以'连接它"。

我在C ++中看到的任何字符串操作问题的每个解决方案都需要重复的多行代码,这些代码只适用于该字符串格式。 我希望能够使用+符号连接任何字符集,或者在C#或Python中使用简单的format()函数。为什么没有简单的选择?

现状

标准输出

我正在编写DLL,到目前为止,我已通过cout运算符将文本输出到<<。到目前为止,使用以下形式的简单char数组,一切都很顺利:

cout << "Hello world!"

运行时字符串

现在谈到我想在运行时构造一个字符串并将其存储为一个类,这个类将包含一个报告一些错误的字符串,以便它们可以被其他类拾取并可能被发送到cout之后,字符串将由函数SetReport(const char* report)设置。所以我真的不想使用多行,所以我继续写下这样的话:

SetReport("Failure in " + __FUNCTION__ + ": foobar was " + foobar + "\n"); // __FUNCTION__ gets the name of the current function, foobar is some variable

我当然立刻得到:

  • expression must have integral or unscoped enum type和......
  • '+': cannot add two pointers

Ugly Strings

右。所以我试图将两个或多个const char*添加到一起,这不是一个选项。所以我发现这里的主要建议是使用std::string,有点奇怪,打字"Hello world!"并不只是给你一个,但让我们给它一个去:

SetReport(std::string("Failure in ") + std::string(__FUNCTION__) + std::string(": foobar was ") + std::to_string(foobar) + std::string("\n"));

辉煌!有用!但看起来有多丑啊!这是我见过的一些最丑陋的代码。我们可以简化这个:

SetReport(std::string("Failure in ") + __FUNCTION__ + ": foobar was " + std::to_string(foobar) + "\n");

仍然可能是我遇到一个简单的单行字符串连接的最糟糕的方式,但现在一切都应该没问题呢?

转换回常量

嗯不,如果您正在处理DLL,我倾向于做很多事情因为我喜欢单元测试所以我需要通过单元测试库导入我的C ++代码,你会发现当您尝试将该报表字符串设置为类的成员变量std::string时,编译器会发出警告:

warning C4251: class 'std::basic_string<_Elem,_Traits,_Alloc>' needs to have dll-interface to be used by clients of class'

我发现除了&#34;忽略警告&#34;(不良做法!)之外的唯一真正的解决方案是使用const char*作为成员变量而不是{{ 1}}但这不是一个真正的解决方案,因为现在你必须将你丑陋的连接(但动态)字符串转换回你需要的const char数组。但是你不能在最后标记std::string(即使你为什么要这样,因为这种连接在第二次变得越来越荒谬?)你必须确保.c_str()没有#&# 39;清理你新建的绳子并留下垃圾。所以你必须在接收字符串的函数中执行此操作:

std::string

哪个是疯了。因为现在我穿过几种不同类型的字符串,使我的代码变得丑陋,添加了比应该需要的更多的行,并且只是为了将一些字符粘在一起。为什么这么难?

解?

那么解决方案是什么?我觉得我应该能够创建一个将const std::string constString = (input); m_constChar = constString.c_str(); 连接在一起但又处理其他对象类型的函数,例如const char*std::stringint,我强烈地感觉到这应该是一行的,但我无法找到任何实现的例子。我是否应该使用double而不是常量变量,即使我已经读过您永远不应该更改char*的值,那么这会有什么帮助?

有没有经验丰富的C ++程序员解决了这个问题,现在对C ++字符串感到满意,你的解决方案是什么?有没有解决方案?这不可能吗?

4 个答案:

答案 0 :(得分:3)

构建字符串,将非字符串类型格式化为字符串的标准方法是string stream

#include <sstream>

std::ostringstream ss;
ss << "Failure in " << __FUNCTION__ << ": foobar was " << foobar << "\n";
SetReport(ss.str());

如果您经常这样做,您可以编写一个可变参数模板来执行此操作:

template <typename... Ts> std::string str(Ts&&...);
SetReport(str("Failure in ", __FUNCTION__, ": foobar was ", foobar, '\n'));

实施留给读者练习。

在这种特殊情况下,字符串文字(包括__FUNCTION__)可以通过简单地一个接一个地连接来连接;并且,假设foobarstd::string,可以使用+与字符串文字连接:

SetReport("Failure in " __FUNCTION__ ": foobar was " + foobar + "\n");

如果foobar是数字类型,您可以使用std::to_string(foobar)进行转换。

答案 1 :(得分:2)

普通字符串文字(例如"abc"__FUNCTION__)和char const*不支持连接。这些只是简单的C风格char const[]char const*

解决方案是使用一些字符串格式化工具或库,例如:

  • std::string并使用+进行连接。可能涉及太多不必要的分配,除非operator+使用表达式模板。
  • std::snprintf。这个不为你分配缓冲区而不是类型安全的,所以人们最终会为它创建包装器。
  • std::stringstream。无处不在和标准,但它的语法充其量是尴尬。
  • boost::format。输入安全,但据报道很慢。
  • cppformat。据说现代又快。

答案 2 :(得分:1)

最简单的解决方案之一是使用 C ++空字符串。这里我声明了一个名为_的空字符串变量,并在字符串连接前使用它。确保你总是把它放在前面。

#include <cstdio>
#include <string>

using namespace std;
string _ = "";

int main() {
        char s[] = "chararray";
        string result =
                _ + "function name = [" + __FUNCTION__ + "] "
                "and s is [" + s + "]\n";
        printf( "%s", result.c_str() );
        return 0;
}

输出:

function name = [main] and s is [chararray]

关于__FUNCTION__,我发现在Visual C ++中它是一个宏,而在GCC中它是一个变量,因此SetReport("Failure in " __FUNCTION__ "; foobar was " + foobar + "\n");只适用于Visual C ++。请参阅:https://msdn.microsoft.com/en-us/library/b0084kay.aspxhttps://gcc.gnu.org/onlinedocs/gcc/Function-Names.html

使用上面的空字符串变量的解决方案应该适用于Visual C ++和GCC。

答案 3 :(得分:0)

我的解决方案

我继续尝试不同的东西,我已经找到了一个解决方案,它结合了tivn的答案,包括制作一个空字符串,以帮助将长std::string和字符数组连接在一起和我自己的函数允许将const char*单行复制到#ifndef _STRINGS_H_ #define _STRINGS_H_ #include <string> // tivn's empty string in the header file extern const std::string _; // My own version of .c_str() which produces a copy of the contents of the string input const char* ToCString(std::string input); #endif ,当字符串对象离开范围时可以安全使用。

我会使用Mike Seymour的可变参数模板,但他们似乎并没有得到我运行的Visual Studio 2012的支持,我需要这个解决方案非常通用所以我可以&# 39;不要依赖它们。

这是我的解决方案:

<强> Strings.h

#include "Strings.h"

const std::string str = "";

const char* ToCString(std::string input)
{
    char* result = new char[input.length()+1];
    strcpy_s(result, input.length()+1, input.c_str());
    return result;
}

<强> Strings.cpp

m_someMemberConstChar = ToCString(_ + "Hello, world! " + someDynamicValue);

<强>用法

Option Explicit

Private Const cMoniker As String = "FormHasBeenDisplayed"

Private Sub DisplayFormIfFirstTime()
If HasBeenOpened = False Then DisplayForm
End Sub

Public Sub DisplayForm()
MsgBox "Ok, its not a form but a dialog box...", vbInformation
End Sub

Public Function HasBeenOpened() As Boolean

Dim oName As Name
On Error Resume Next
Set oName = Application.Names(cMoniker)
On Error GoTo 0

If Not oName Is Nothing Then
    HasBeenOpened = True
Else
    Call Application.Names.Add(cMoniker, True, False)
End If

End Function

'Call this to remove the flag...
Public Sub ResetOpenOnce()
On Error Resume Next
Application.Names(cMoniker).Delete
End Sub

我认为这很整洁,在大多数情况下都适用。谢谢大家帮助我。