天道酬勤,学无止境

为什么不允许从字符数组初始化 std::string?(Why not allowing std::string initialization from array of chars?)

问题

在 C++ 中,您可以从char *const char *初始化std::string对象,这隐含地假定字符串将在指针之后找到的第一个NUL字符处结束。

然而,在 C++ 中,字符串文字是数组,即使字符串文字包含嵌入的NUL ,也可以使用模板构造函数来获得正确的大小。 例如,请参见以下玩具实现:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>

struct String {
    std::vector<char> data;
    int size() const { return data.size(); }

    template<typename T> String(const T s);

    // Hack: the array will also possibly contain an ending NUL
    // we don't want...
    template<int N> String(const char (&s)[N])
        : data(s, s+N-(N>0 && s[N-1]=='\0')) {}

    // The non-const array removed as probably a lot of code
    // builds strings into char arrays and the convert them
    // implicitly to string objects.
    //template<int N> String(char (&s)[N]) : data(s, s+N) {}
};

// (one tricky part is that you cannot just declare a constructor
// accepting a `const char *` because that would win over the template
// constructor... here I made that constructor a template too but I'm
// no template programming guru and may be there are better ways).
template<> String::String(const char *s) : data(s, s+strlen(s)) {}

int main(int argc, const char *argv[]) {
    String s1 = "Hello\0world\n";
    printf("Length s1 -> %i\n", s1.size());
    const char *s2 = "Hello\0world\n";
    printf("Length s2 -> %i\n", String(s2).size());
    std::string s3 = "Hello\0world\n";
    printf("std::string size = %i\n", int(s3.size()));
    return 0;
}

是否有任何特定的技术原因导致标准没有考虑这种方法,而是嵌入NUL的字符串文字在用于初始化std::string对象时最终被截断?

回答1

C++14 为字符串文字引入了后缀,使它们成为std::string对象,因此主要用例不再相关。

#include <iostream>
#include <string>
using namespace std;
using namespace std::literals;

int main() {
    string foo = "Hello\0world\n";
    string bar = "Hello\0world\n"s;
    cout << foo.size() << " " << bar.size() << endl; // 5 12
    cout << foo << endl; // Hello
    cout << bar << endl; // Helloworld
    return 0;
}
回答2

用包含嵌入空字节的文字初始化std::string需要将起始指针和长度都传递给构造函数。

如果有一个专门的 take-array-reference 构造函数模板,那是最简单的,但正如你所注意到的

  • 这样的模板,只有数组参数,将被认为比构造函数只采用char const*更糟糕,并且

  • 是否应该包含最终终止的空值尚不清楚。

第一点意味着物理代码接口将是单个模板化构造函数,其中只有文档(而不是您的编辑器的工具提示)会讲述它接受或不接受的完整故事。 一种解决方法是引入一个额外的虚拟解析器参数。 这降低了便利性。

第二点是引入错误的机会。 构造函数最常见的用途无疑是普通的字符串文字。 然后,不时地,它将用于嵌入空字节的文字和/或数组,但奇怪的是最后一个字符被砍掉了。

相反,人们可以简单地首先命名该值,

char const data[] = "*.com\0*.exe\0*.bat\0*.cmd\0";
string s( data, data + sizeof( data ) );    // Including 2 nulls at end.

综上所述,当我定义了自己的字符串类时,我已经包含了 take-array-argument 构造函数,但是出于与方便不同的原因。 也就是说,在文字的情况下,字符串对象可以简单地保留该指针,而无需复制,这不仅提供了效率,而且还为例如异常提供了安全性(正确性)。 一个const char数组是我们在 C++11 及更高版本中最清楚的文字指示。

但是, std::string不能这样做:它不是为它设计的。


如果经常这样做,那么可以定义一个这样的函数:

using Size = ptrdiff_t;

template< Size n >
auto string_from_data( char const (&data)[n] )
    -> std::string
{ return std::string( data, data + n ); }

然后可以只写

string const s = string_from_data( "*.com\0*.exe\0*.bat\0*.cmd\0" );

免责声明:编译器没有接触或看到任何代码。


[我在第一次写作时错过了这一点,但赫尔基尔的回答提醒了我。 现在去喝咖啡!]

C ++ 14 字符串类型文字切断了最终的\0 ,因此对于这样的文字,上面必须明确包含终止的空值:

string const s = "*.com\0*.exe\0*.bat\0*.cmd\0\0"s;

除此之外,C++14 字符串类型文字似乎提供了所寻求的便利。

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 为什么不允许通过检索到的指向其数据的指针修改字符串?(Why is modifying a string through a retrieved pointer to its data not allowed?)
    问题 在C ++ 11中,必须连续存储std::string的字符,如第21.4.1 / 5节所述: basic_string对象中的类似于char的对象应连续存储。 也就是说,对于任何basic_string对象s,标识&*(s.begin()+ n)==&* s.begin()+ n对于n的所有值均应成立,以使0 <= n <s.size ()。 但是,这是§21.4.7.1列出两个函数的方法,以检索指向基础存储的指针(重点是我的): const charT * c_str()const noexcept; const charT * data()const noexcept; 1返回:一个指针p,使得[0,size()]中的每个i都具有p + i ==&operator [](i)。 2复杂度:恒定时间。 3要求:程序不得更改存储在字符数组中的任何值。 对于第3点,我可以想到的一种可能性是,指针可能由于以下对象的使用而失效(第21.4.1 / 6节): 作为任何标准库函数的参数,并将对非const basic_string的引用作为参数。 在front,back,begin,rbegin,end和rend处调用非const成员函数(operator []除外)。 即使这样,迭代器也可能变得无效,但是我们仍然可以对其进行修改,直到它们生效为止。 我们仍然可以使用指针
  • 为什么编译器不允许在联合体内部使用std :: string?(Why compiler doesn't allow std::string inside union?)
    问题 我想在联盟中使用字符串。 如果我写如下 union U { int i; float f; string s; }; 编译器给出错误,提示U :: S具有复制构造函数。 我阅读了其他文章,以了解解决此问题的替代方法。 但是我想知道为什么编译器最初不允许这样做? 编辑:@KennyTM:在任何联合中,如果成员初始化,其他成员将具有垃圾值,如果未初始化成员,则所有成员将具有垃圾值。 我认为,带标签的联合会为从联合会访问有效值提供一些安慰。 您的问题:您或编译器如何在没有额外信息的情况下为上述联合编写副本构造函数? sizeof(string)给出4个字节。 基于此,编译器可以比较其他成员的大小并分配最大的分配(在我们的示例中为4字节)。 内部字符串的长度无关紧要,因为它将存储在单独的位置。 令字符串为任意长度。 Union所需要知道的就是调用带有字符串参数的字符串类副本构造函数。 无论哪种方式,编译器发现在正常情况下都必须调用复制构造函数,即使在字符串位于Union内部时,也要遵循类似的方法。 所以我认为编译器可以这样做,分配4个字节。 然后,如果将任何字符串分配给s,则字符串类将使用其自己的分配器来分配和复制该字符串。 因此,也没有发生内存损坏的机会。 编译器中联合开发时字符串不存在吗? 因此,答案仍然不清楚。 是这个网站的新加入者,如果有什么问题,请问。 回答1 想一想。
  • 为什么使用 = 来初始化 C++ 中的原始类型?(Why use = to initialise a primitive type in C++?)
    问题 在我工作的地方,人们大多认为最好使用 C++ 风格的构造(带括号)初始化对象,而应该使用 = 运算符初始化原始类型: std::string strFoo( "Foo" ); int nBar = 5; 不过,似乎没有人能够解释为什么他们更喜欢这种方式。 我可以看到std::string = "Foo"; 将是低效的,因为它会涉及一个额外的副本,但是完全消除=运算符并在任何地方使用括号有什么问题? 这是一个共同的约定吗? 背后的想法是什么? 回答1 除非您已经证明它对性能很重要,否则我不会担心在您的示例中使用赋值运算符( std::string foo = "Foo"; )的额外副本。 如果您查看优化代码后该副本甚至存在,我会感到非常惊讶,我相信它实际上会调用适当的参数化构造函数。 在回答你的问题时,是的,我会说这是一个非常普遍的约定。 传统上,人们使用赋值来初始化内置类型,并且没有令人信服的理由来改变传统。 考虑到它对最终代码的影响很小,可读性和习惯是这个约定的完全正当理由。 回答2 使用 = 运算符或构造函数调用初始化变量在语义上是相同的,这只是风格问题。 我更喜欢 = 操作符,因为它读起来更自然。 使用 = 操作符通常不会生成额外的副本——它只是调用普通的构造函数。 但是请注意,对于非原始类型,这仅适用于与声明同时发生的初始化。 比较: std::string
  • 为什么C ++不支持函数返回数组?(Why doesn't C++ support functions returning arrays?)
    问题 某些语言使您能够像正常函数一样声明一个函数,该函数返回一个数组,例如Java: public String[] funcarray() { String[] test = new String[]{"hi", "hello"}; return test; } 为什么C ++不支持int[] funcarray(){} ? 您可以返回一个数组,但是创建这样的函数确实很麻烦。 而且,我在某处听说字符串只是char的数组。 因此,如果您可以用C ++返回一个字符串,为什么不使用数组呢? 回答1 简而言之,我猜这只是一个设计决定。 更具体地说,如果您真的想知道为什么,则需要从头开始。 让我们首先考虑一下C。 在C语言中,“按引用传递”和“按值传递”之间有明显的区别。 简单地说,C语言中的数组名称实际上只是一个指针。 对于所有意图和目的,区别(通常)归结为分配。 代码 int array[n]; 会在堆栈上创建4 * n字节的内存(在32位系统上),并且与声明该代码块的范围相关。 反过来, int* array = (int*) malloc(sizeof(int)*n); 将创建相同数量的内存,但是在堆上。 在这种情况下,内存中的内容与范围无关,只有对内存的引用受范围的限制。 这就是按值传递和按引用传递的地方。如您所知,按值传递意味着将某些内容传递给函数或从函数返回时,传递的“事物
  • int a [] = {1,2,}; 允许使用怪异的逗号。 有什么特殊原因吗?(int a[] = {1,2,}; Weird comma allowed. Any particular reason?)
    问题 也许我不是来自这个星球,但是在我看来,以下内容应该是语法错误: int a[] = {1,2,}; //extra comma in the end 但事实并非如此。 当在Visual Studio中编译的代码我很惊讶,但我已经学会了不信任MSVC编译器尽可能C ++的规则而言,所以我检查的标准,它是标准允许为好。 如果您不相信我,可以看到8.5.1的语法规则。 为什么允许这样做? 这可能是一个愚蠢的无用问题,但我想让您理解我为什么要问。 如果这是一般语法规则的子情况,我会理解-他们决定不为了简化通用语法而仅仅为了在初始化列表的末尾不允许多余的逗号。 但是不可以,其他逗号是明确允许的。 例如,不允许在函数调用参数列表的末尾(当函数采用... )使用多余的逗号,这是正常现象。 因此,再次,是否有任何特定原因明确允许使用此冗余逗号? 回答1 它使生成源代码和编写可在以后轻松扩展的代码变得更加容易。 考虑将额外的条目添加到以下内容所需的条件: int a[] = { 1, 2, 3 }; ...您必须将逗号添加到现有行并添加新行。 相比之下,与其中三个已经有后一个逗号,你只需要添加一个线的情况下。 同样,如果您要删除一行,则可以这样做,而不必担心它是否是最后一行,并且可以对行进行重新排序而不必担心逗号。 基本上,这意味着您对待线条的方式是一致的。 现在考虑生成代码。 类似于(伪代码
  • 为什么C ++不使用std :: nested_exception允许从析构函数中抛出?(Why doesn't C++ use std::nested_exception to allow throwing from destructor?)
    问题 从析构函数中抛出异常的主要问题是,在调用析构函数时,另一个异常可能是“在飞行中”( std::uncaught_exception() == true ),因此在这种情况下该怎么办并不明显。 用新异常“覆盖”旧异常将是处理这种情况的一种可能方法。 但已决定在这种情况下必须调用std::terminate (或另一个std::terminate_handler )。 C ++ 11通过std::nested_exception类引入了嵌套异常功能。 此功能可用于解决上述问题。 可以将旧的(未捕获的)异常嵌套到新的异常中(反之亦然?),然后可以抛出该嵌套的异常。 但是这个想法没有被使用。 在这种情况下,C ++ 11和C ++ 14仍会调用std::terminate 。 这样的问题。 是否考虑过嵌套异常的想法? 有什么问题吗? C ++ 17中的情况不会改变吗? 回答1 您引用的问题发生在您的析构函数作为堆栈展开过程的一部分执行时(当您的对象不是作为堆栈展开的一部分创建时) 1 ,并且您的析构函数需要发出异常。 那么它是如何工作的呢? 你有两个例外。 异常X是导致堆栈展开的异常。 异常Y是析构函数想要抛出的异常。 nested_exception只能容纳其中之一。 所以也许你有异常Y包含一个nested_exception (或者可能只是一个exception_ptr )。
  • 为什么 GCC 只是通过将其放入循环中而被欺骗以允许未定义的行为?(Why is GCC tricked into allowing undefined behavior simply by putting it in a loop?)
    问题 以下是荒谬的,但可以用g++ -Wall -Wextra -Werror -Winit-self干净地编译(我测试了 GCC 4.7.2 和 4.9.0): #include <iostream> #include <string> int main() { for (int ii = 0; ii < 1; ++ii) { const std::string& str = str; // !! std::cout << str << std::endl; } } 标线!! 导致未定义的行为,但未被 GCC 诊断。 但是,注释掉for行会使 GCC 抱怨: error: ‘str’ is used uninitialized in this function [-Werror=uninitialized] 我想知道:为什么GCC在这里这么容易上当? 当代码不在循环中时,GCC 知道它是错误的。 但是将相同的代码放在一个简单的循环中,GCC 就不再理解了。 这让我很困扰,因为当我们在 C++ 中犯了愚蠢的错误时,我们非常依赖编译器来通知我们,但它在一个看似微不足道的情况下却失败了。 奖金琐事: 如果将std::string更改为int并打开优化,GCC 将诊断错误,即使使用循环。 如果您使用-O3构建损坏的代码,GCC 会使用字符串参数的空指针调用 ostream 插入函数。
  • 为什么可以将const char *分配给char *?(Why is it possible to assign a const char* to a char*?)
    问题 我知道例如"hello"的类型为const char* 。 所以我的问题是: 我们如何将像"hello"这样的文字字符串分配给非const char*如下所示: char* s = "hello"; // "hello" is type of const char* and s is char* // and we know that conversion from const char* to // char* is invalid 是像"hello"这样的文字字符串会占用我所有程序的内存,还是就像临时变量在语句结束时被销毁一样? 回答1 实际上, "hello"是char const[6]类型。 但是问题的实质仍然是正确的-为什么C ++允许我们将只读内存位置分配给非const类型? 这样做的唯一原因是对不知道const旧C代码的向后兼容性。 如果C ++在这里严格,它将破坏很多现有代码。 也就是说,大多数编译器都可以配置为警告不推荐使用的代码,甚至默认情况下也可以发出警告。 此外,C ++ 11完全不允许这样做,但是编译器可能尚未强制执行。 对于斯坦达迪斯球迷: [参考资料1] C ++ 03标准:§4.2/ 2 可以将不是宽字符串文字的字符串文字(2.13.4)转换为“ pointer to char”类型的右值; 宽字符串文字可以转换为“指向wchar_t的指针
  • 为什么此const成员函数允许修改成员变量?(Why does this const member function allow a member variable to be modified?)
    问题 class String { private: char* rep; public: String (const char*); void toUpper() const; }; String :: String (const char* s) { rep = new char [strlen(s)+1]; strcpy (rep, s); } void String :: toUpper () const { for (int i = 0; rep [i]; i++) rep[i] = toupper(rep[i]); } int main () { const String lower ("lower"); lower.toUpper(); cout << lower << endl; return 0; } 回答1 const成员函数是一个不改变其成员变量的成员函数。 成员函数上的const并不意味着const char *。 这意味着您无法更改指针所拥有地址中的数据。 您的示例不会更改成员变量本身。 成员函数上的const将确保您将所有成员变量都视为const。 这意味着如果您具有: int x; char c; char *p; 然后您将拥有: const int x; const char c; char * const p; //<-- means you
  • 为什么switch语句不能应用于字符串?(Why the switch statement cannot be applied on strings?)
    问题 编译以下代码,并得到type illegal的错误。 int main() { // Compilation error - switch expression of type illegal switch(std::string("raj")) { case"sda": } } 您不能在switch或case使用string。 为什么? 有什么解决方案可以很好地支持类似于打开字符串的逻辑吗? 回答1 之所以与类型系统有关。 C / C ++并不真正支持将字符串作为一种类型。 它确实支持常量char数组的概念,但实际上并没有完全理解字符串的概念。 为了生成switch语句的代码,编译器必须了解两个值相等的含义。 对于像int和enums这样的项目,这是一个微不足道的比较。 但是编译器应该如何比较2个字符串值? 区分大小写,不区分大小写,文化意识等。。。如果不完全了解字符串,则无法准确回答。 此外,C / C ++ switch语句通常作为分支表生成。 为字符串样式开关生成分支表并不是那么容易。 回答2 如前所述,编译器喜欢建立查找表,以尽可能地将switch语句优化为接近O(1)的时序。 将此与C ++语言没有字符串类型的事实结合在一起std::string是标准库的一部分,而标准库本身不是该语言的一部分。 我将提供您可能要考虑的替代方法,我过去一直使用它以取得良好效果。
  • 为什么对于不能TriviallyCopyable的对象,std :: memcpy的行为将无法定义?(Why would the behavior of std::memcpy be undefined for objects that are not TriviallyCopyable?)
    问题 从http://en.cppreference.com/w/cpp/string/byte/memcpy: 如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则该行为是不确定的。 在我的工作中,很长时间以来,我们一直使用std::memcpy来按位交换不是TriviallyCopyable的对象,方法是: void swapMemory(Entity* ePtr1, Entity* ePtr2) { static const int size = sizeof(Entity); char swapBuffer[size]; memcpy(swapBuffer, ePtr1, size); memcpy(ePtr1, ePtr2, size); memcpy(ePtr2, swapBuffer, size); } 从来没有任何问题。 我知道,将std::memcpy与非TriviallyCopyable对象一起使用并导致下游未定义行为是微不足道的。 但是,我的问题是: 与非TriviallyCopyable对象一起使用时,为什么std::memcpy本身的行为是不确定的? 为什么标准认为有必要对此加以说明? 更新 针对该帖子和该帖子的答案,已修改了http://en.cppreference.com/w/cpp/string/byte
  • 为字符数组赋值[重复](Assigning value to char array [duplicate])
    问题 这个问题在这里已经有了答案: 在 C 中为字符数组分配一个值2 个回答 7年前关闭。 我输入时没有错误 char a[10] = "Hi"; 但是当我将其更改为以下内容时,出现错误: Array type char is not assignable. char a[10]; a = "Hi"; 为什么数组类型 char 不可分配? 是因为语言是故意这样写的还是我遗漏了一点? 回答1 数组不是可修改的左值 用 char a[10]; strcpy(a, "Hi"); 回答2 正如我在上面评论的那样,C++ 这样做的方法是使用std::string而不是char[] 。 这将为您提供您期望的分配行为。 也就是说,您只在第二种情况下收到错误的原因是这两行中的=表示不同的意思: char a[10] = "Hi"; a = "Hi"; 第一个是初始化,第二个是赋值。 第一行在堆栈上分配足够的空间来容纳 10 个字符,并将这些字符的前三个初始化为 'H'、'i' 和 '\0'。 从这点开始, a所做的就是引用数组在堆栈上的位置。 因为数组只是栈上的一个地方, a永远不允许改变。 如果您希望堆栈上的不同位置保存不同的值,则需要不同的变量。 另一方面,第二行(无效)尝试将a更改为引用"Hi"的(技术上不同的)咒语。 由于上述原因,这是不允许的。 一旦你有一个初始化的数组
  • 为什么我不能在传递给参数时隐式构造一个给定合适构造函数的对象?(Why can't I implicitly construct an object given a suitable constructor when passing to an argument?)
    问题 在下面的示例中,为什么我不能简单地将string传递给printFoo() ? #include <string> #include <iostream> using namespace std; class Foo { public: Foo(const Foo &foo) : str(foo.str) {} Foo(string str) : str(str) {} string str; }; void printFoo(Foo foo) { cout << foo.str << endl; } int main() { Foo foo("qux"); printFoo(foo); // OK printFoo("qix"); // error: no matching function for call to 'printFoo' return 0; } 无论出于何种原因,我都认为构造函数会被自动确定并用于构造对象。 为什么我不能这样做,但我可以将char[n]常量传递给接受std::string的参数,例如? 回答1 将涉及两个隐式转换: 到std::string 到Foo C++ 最多做一个: 从 4 个标准转换 (N3337) 标准转换是具有内置含义的隐式转换。 第 4 条列举了完整的此类转换集。 标准转换序列是按以下顺序进行的标准转换序列: —
  • 添加字符串的这两种情况有什么区别?(What is the difference between these two cases of adding a string?)
    问题 我注意到当我初始化一个字符串时,编译器报告了一个我没有预料到的错误。 例如: #include <iostream> #include <string> using namespace std; int main() { string s1 = "Hello", s2 = "World!"; // ok string s3 = s1 + ", " + "World!"; // ok string s4 = "Hello" + ", " + s2; // error cout << s1 + " " + s2 << endl; //ok return 0; } 对我来说,如果s3工作得很好, s4应该这样做。 为什么我会收到那个错误? 这两个初始化字符串( s3和s4 )有什么区别? 回答1 "Hello"不是std::string (而是一个const char[6] ,而", "是一个const char[3] )并且std::string s 的+运算符不适用。 这是 C++ 的一个小不便,源于它的 C 祖先。 这意味着在通过+连接字符串时,您必须确保其两个操作数中的至少一个实际上是std::string ,如 auto s = std::string{"Hello"} + ", " + "World"; 其中第一个+有一个std::string作为它的第一个操作数
  • main() std::string 而不是 char** 为什么要排除(main () std::string instead of char** why the exclusion)
    问题 编译器给出警告warning: second argument of 'int main(int, std::string*)' should be 'char **' [-Wmain] 当我选择投入时 int main(int argv, std::string a[]) 代替 int main(int argv, char * argc []) 如果你能想出一个理由,也请多说一下字符串方法有什么问题。 我的意思是 std::string 是字符表示/字符串表示的后裔,对于 C++ 来说,为什么要为 C 风格烦恼呢? 还 标准实现真的没有黑客吗? 回答1 原因很简单:语言规则不强制您的表单,您使用的实现不通过自己的选择支持它。 引用标准 3.6.1p2 实现不应预定义主函数。 该函数不得重载。 它应该有一个 int 类型的返回类型,否则它的类型是实现定义的。 所有实现都应允许以下两个 main 定义: int main() { /* ... */ } 和 int main(int argc, char* argv[]) { /* ... */ } 在后一种形式中,argc 应该是从程序运行的环境中传递给程序的参数数量。 如果 argc 非零,这些参数应在 argv[0] 到 argv[argc-1] 中作为指向空终止多字节字符串 (NTMBS) (17.3.2.1.3.2)
  • 数组的unique_ptr有什么用吗?(Is there any use for unique_ptr with array?)
    问题 std::unique_ptr has support for arrays, for instance: std::unique_ptr<int[]> p(new int[10]); but is it needed? probably it is more convenient to use std::vector or std::array. Do you find any use for that construct? 回答1 有些人甚至没有使用std::vector ,甚至没有分配器。 某些人需要动态调整大小的数组,因此无法使用std::array 。 有些人从其他已知返回数组的代码中获取数组。 而且该代码不会被重写以返回vector或其他内容。 通过允许unique_ptr<T[]> ,您可以满足这些需求。 简而言之,在需要时使用unique_ptr<T[]> 。 当替代品根本无法为您服务时。 这是不得已的工具。 回答2 需要权衡取舍,然后选择与您想要的解决方案相匹配的解决方案。 从我的头顶上: 初始尺寸 vector和unique_ptr<T[]>允许在运行时指定大小 array仅允许在编译时指定大小 调整大小 array和unique_ptr<T[]>不允许调整大小 vector确实 贮存 vector和unique_ptr<T[]>将数据存储在对象外部
  • 为什么在 C++ 中你更喜欢 char* 而不是 string?(Why do you prefer char* instead of string, in C++?)
    问题 我是一名 C 程序员,正在尝试编写 C++ 代码。 我听说 C++ 中的string在安全性、性能等方面优于char* ,但有时似乎char*是更好的选择。 有人建议程序员不要在 C++ 中使用char* ,因为我们可以做char*可以用 string 做的所有事情,而且它更安全、更快。 你曾经在 C++ 中使用过char*吗? 具体条件是什么? 回答1 使用std::string更安全,因为您无需担心为字符串分配/取消分配内存。 C++ std::string类很可能在内部使用char*数组。 但是,该类将为您管理内部数组的分配、重新分配和解除分配。 这消除了使用原始指针带来的所有常见风险,例如内存泄漏、缓冲区溢出等。 此外,它也非常方便。 您可以复制字符串、附加到字符串等,而无需手动提供缓冲区空间或使用 strcpy/strcat 之类的函数。 使用 std::string 就像使用=或+运算符一样简单。 基本上,它是: std::string s1 = "Hello "; std::string s2 = s1 + "World"; 相对... const char* s1 = "Hello"; char s2[1024]; // How much should I really even allocate here? strcpy(s2, s1); strcat(s2
  • 从 char(不是 char*)到 std::string 的首选转换(Preferred conversion from char (not char*) to std::string)
    问题 我有一个char ,一个普通的旧字符,我想把它变成一个std::string 。 std::string(char)当然不存在。 我可以创建一个字符数组并将其复制进去,我可以通过字符串流或许多其他小的迂回路线。 目前,我更喜欢boost::lexical_cast ,但即使这样对于这个简单的任务来说似乎也太冗长了。 那么什么是首选方式? 回答1 std::string有一个构造函数,它接受一个数字和一个字符。 角色将重复给定的次数。 因此,您应该使用: std::string str(1, ch); 回答2 要添加到答案中,您可以简单地使用初始化列表 std::string str = {ch}; 回答3 只使用带字符的重载? 即string(1, 'A') 回答4 您仍然可以使用带有两个迭代器的字符串构造函数: char c = 'x'; std::string(&c, &c + 1); 更新: 詹姆斯和 GMan 的好问题。 刚刚在 Derek M. Jones 的可免费下载的“The New C Standard”中搜索“pointer past”,我的第一个搜索结果是: 如果表达式 P 指向数组对象的一个​​元素,而表达式 Q 指向同一数组对象的最后一个元素,则指针表达式 Q+1 比较大于 P...即使 Q+1 不指向一个数组对象的元素... 在分段体系结构上
  • 静态 const 成员变量初始化(static const member variable initialization)
    问题 看起来我可以初始化一个 POD 静态常量成员,但不能初始化其他类型: struct C { static const int a = 42; // OK static const string b = "hi"; // compile error }; 为什么? 回答1 initializer in the class definition的语法initializer in the class definition值设定项仅允许用于整型和枚举类型。 对于std::string ,它必须在类定义之外定义并在那里初始化。 struct C { static const int a = 42; static const string b; }; const string C::b = "hi"; // in one of the .cpp files static members必须在一个翻译单元中定义才能满足一个定义规则。 如果 C++ 允许下面的定义; struct C { static const string b = "hi"; }; b将在包含头文件的每个翻译单元中定义。 C++ 只允许在类声明中定义integral或enumeration类型的const static数据成员作为捷径。 不能定义其他类型的const static数据成员的原因是需要非平凡的初始化
  • 为什么不能使用'='运算符将数组变量直接分配给另一个数组变量?(Why can't I assign an array variable directly to another array variable with the '=' operator?)
    问题 为什么以下分配不起作用? 如果可能的话,我想作一个低级的解释。 另外,这是我得到的编译器错误:将'char *'分配给'char [20]'时类型不兼容 class UCSDStudent { char name[20]; public: UCSDStudent( char name[] ) { //this-> name = name; does not work! Please explain why not strcopy( this -> copy, copy ); //works } }; 回答1 因为当有这样的函数调用UCSDStudent( char name[] )仅复制数组name的地址而不是整个数组。 这是C \ C ++功能。 此外,该name被定义为char name [20]是不修改的左值。 关于strcpy :它将带来未定义的行为,就好像您的源数组没有NULL字符一样,它也会将一些垃圾内容也复制到this->name 。 您可以在此处阅读有关strcpy更多信息 回答2 如果仍要为数组分配数组,请使用循环。 例如:class UCSDStudent { char name[20]; public: UCSDStudent( char name[] ) { for(int i = 0; i<20; i++) { this-> name[i] =