【问题标题】:C-Style strings to std::string conversion clarificationC 样式字符串到 std::string 转换说明
【发布时间】:2012-04-11 15:44:25
【问题描述】:

我有几个问题,我认为对于具有 C++ 经验的人来说很容易回答,我会为 TL 加粗问题;DR

给定以下代码:

void stringTest(const std::string &s)
{
    std::cout << s << std::endl;
}

int main()
{
    stringTest("HelloWorld");
}

希望有人可以在这里指出我思考过程中的错误:

为什么 stringTest 中的参数在传递 C-Style 字符串时必须标记为 const? 使用其 cstyle 字符串构造函数是否会隐式转换为 std::string ,因此“s”不再是对文字的引用(并且不需要是 const)。

此外,cstyle 字符串构造函数是什么样的,以及编译器如何知道在看到时调用它

stringTest("HelloWorld");

它是否简单地将字符串文字识别为 char* 之类的东西?

我在研究复制构造函数时偶然发现了这些问题。我自己澄清的另一个快速问题......

如果是这样的情况:

std::string s = "HelloWorld";

cstyle字符串构造函数是不是用来实例化一个临时std::string,然后用字符串拷贝构造函数把临时字符串复制到"s"中?

std::string(const std::string&);

【问题讨论】:

标签: c++ string copy


【解决方案1】:

为什么 stringTest 中的参数在传递 C-Style 字符串时必须标记为 const?

仅当参数为引用时才需要,因为临时 std::string 是从您传入的 char const* 构造的,而对临时的非 const 引用是非法的。

它是否简单地将字符串文字识别为 char* 之类的东西?

字符串文字是一个char const 数组,它衰减为char const*。由此,编译器推断它应该使用非explicit 构造函数std::string::string(char const *) 来构造临时对象。

cstyle构造函数是用来实例化一个临时std::string,然后用字符串拷贝构造函数把临时字符串复制到"s"吗?

比这要复杂一些。是的,创建了一个临时的。但是复制构造函数可能会也可能不会被调用;允许编译器跳过复制构造作为优化。但是,仍然必须提供复制构造函数,因此无法编译以下代码:

class String {
    String(char const *) {}
  private:
    String(String const &);
};

int main()
{
    String s = "";
}

此外,在 C++11 中,将使用移动构造函数(如果提供);在这种情况下,不需要复制构造函数。

【讨论】:

  • +1 但你可能(应该)澄清在第三种情况下,即使复制构造函数是必需的,它实际上并没有被调用,因为它会(总是——不管优化?)被省略。这是相当重要的,即使它“只是”一种允许的优化。
  • 字符串文字没有char const*,是char const[N],当然可以隐式转换为char const*
  • @KonradRudolph:这样做并扩展了复制/移动构造函数的内容。
  • @Riken:需要复制构造函数作为向编译器发出可以复制类型对象的信号;使其成为private 使它们不可复制。它被忽略为一种优化,但是是的,这取决于编译器。所以,永远不要在复制构造函数中做任何花哨的事情。
  • @Riken 理论上它依赖于编译器;实际上,所有现代编译器在任何地方都可靠地实现它,因为它是一个非常重要且相当简单的优化,并且标准明确允许它(注意:除非编译器可以“证明”效果相同,否则不能轻易省略其他调用) .只有复制构造函数才允许这样做。但是复制构造函数仍然是允许的,因为一个类可能故意(!)禁止通过使复制构造函数不可访问(=私有)来禁止这种行为。
【解决方案2】:

它是否简单地将字符串文字识别为类似于 字符*?

原始问题的这一部分没有像我希望的那样清楚地回答。不过,我完全赞同(并投票赞成)约塞连对其余部分的回答。

基本上,您需要了解编译器在看到代码中的字符串文字时在做什么。该字符数组(实际上是任何 c 样式的字符串)实际上存储在与其所属的代码完全不同的位置(取决于体系结构,数字文字可以作为程序集的一部分存储在该位置本身/二进制指令)。这里的两个代码块“或多或少”等效(忽略缺少包含或命名空间声明):

int main(void)
{
    cout << "Hello!" << endl;
    return 0;
}

这更接近“真正”发生的事情:

const char HELLO_STR[] = { 'H', 'e', 'l', 'l', 'o', '!', 0 };

int main(void)
{
    cout << HELLO_STR << endl;
    return 0;
}

如果我在数组 init 或其他任何内容中犯了错误,请原谅我,但我认为这表达了我的意思,即字符串文字“真正”存储在哪里。它不是内联的,但对于定义它的程序的另一部分来说是一个不可见的常量。此外,那里的一些(大多数?)编译器还将字符串文字“一起”排列,这样如果您在 50 个地方使用相同的文字,它只存储其中一个,并且所有这些都引用同一个常量,节省内存。

所以请记住,任何时候您使用字符串文字时,您都在使用“隐形”存在于某处的 const char[N],它被隐式转换为 const char*。

【讨论】:

    【解决方案3】:

    为什么 stringTest 中的参数在传递 C-Style 字符串时必须标记为 const?

    编辑: 临时对象必须是不可变的。看larsman的评论和回答,他是对的。

    原因很简单:

    void change(std::string& c) { c = "abc"; }
    change("test"); // what should the code exactly do??
    

    此外,cstyle 字符串构造函数是什么样的,编译器如何知道在看到时调用它:

    它为string(char*) 构造函数查找std::string

    如果是这样的情况:

    std::string s = "HelloWorld";
    

    cstyle构造函数是不是用来实例化一个临时std::string,然后用字符串拷贝构造函数把临时字符串复制到"s"中?: std::string(const std::string&);

    没有。在这种确切的情况下(TYPE variable = SOMETHING),它与写TYPE variable(SOMETHING); 相同。因此,没有使用复制。

    【讨论】:

    • const&amp; 存在是因为对临时对象的可变引用是非法的,而不是因为构造函数不是explicit
    • @KonradRudolph,我在 MSVC 中未启用任何优化的情况下进行了尝试,TYPE variable=SOMETHING; 运行了构造函数。我认为该行为在某处被描述,只是找不到它
    【解决方案4】:

    const string &amp; s 在此示例中是从参数“HelloWorld”调用构造函数所必需的。使用的构造函数是类型转换构造。

    string&amp; s 不会这样做,因为 s 直接引用了一个字符串对象。

    类型转换的定义类似于

     basic_string(const _CharT* __s);
    

    使用类型定义

    typedef basic_string<char>    string; 
    

    因此声明将评估为

    basic_string(const char * __s)    
    

    【讨论】:

    • 听起来错了。要么你错了,要么解释不清楚。无论哪种方式,const不需要调用复制构造函数。
    • @KonradRudolph 听起来不对,因为它是错误的,我的思维能力倒退了。
    猜你喜欢
    • 1970-01-01
    • 2011-01-15
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    • 2018-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多