括号初始化 (C++)

5

我目前正在学习C++,使用的是C++ Primer Plus。但我想查看cplusplus网站,跳过一些内容直接学习文件处理。

我从Java、PHP和Visual Basic等编程语言中了解了文件处理的基础知识。但我遇到了一行非常奇怪的代码。

ostream os(&fb);

fb代表了一个filebuf。我不太理解这个语法,但是我可以猜出它与以下语法相同:

ostream os = &fb;

但我从未真正阅读过这种初始化变量的方式。

所以我想知道。我是不是无意义地忽略了一个真正有用的功能?这种初始化方式只是旧的吗?还是有所区别?

先感谢您。

5个回答

6
两种形式都执行初始化。第一种语法(带有())称为直接初始化语法。第二种语法(带有=)称为复制初始化语法。它们在大多数实际情况下的作用相同,但确实存在两者之间的差异。
当左侧(LHS)和右侧(RHS)的类型相同时(忽略任何const/volatile限定符),它们确实完全相同。语言标准明确规定,在这种情况下,=形式等效于()形式。
但是当类型不同时(且LHS类型是类类型)时,这两种形式通常会有不同的工作方式。
  • 复制初始化形式的工作方式如下:将RHS值转换为LHS类型的临时对象(通过任何可能的方法:标准转换、转换运算符、转换构造函数)。然后使用LHS类的复制构造函数将临时对象复制到LHS对象。

  • 直接初始化形式的工作方式如下:只需考虑LHS的所有构造函数,并通过使用重载解析选择最合适的构造函数。

您可以立即注意到,复制初始化语法无条件地使用复制构造函数(复制和中间临时对象可以被优化掉,但在概念上它们存在)。如果LHS类没有可访问的复制构造函数,则复制初始化无条件地变为不合法,而直接初始化可能仍然有效。
此外,应用于某些构造函数的关键字explicit将影响对于哪些类型组合可用哪种初始化形式。

1
我只想强调一下“explicit”这个词(因为答案听起来像是一个非常标准的答案):如果复制构造函数是显式的,那么在LHR和RHS是相同类型的情况下,=语法将失败,而()形式不会失败。 - Johannes Schaub - litb

5
一个小程序,用于查看何时调用复制构造函数和何时调用重载赋值运算符函数:
#include <iostream>

using namespace std;

class test
{
    public:
        // default constructor.
        test()
        {
            cout<<"Default Ctor called"<<endl;
        }

        // copy constructor.
        test(const test& other)
        {
            cout<<"Copy Ctor called"<<endl;
        }

        // overloaded assignment operator function.
        test& operator=(const test& other)
        {
            cout<<"Overload operator function called"<<endl;
            return *this;
        }
};

int main(void) 
{
    test obj1;  // default constructor called.

    test obj2 = obj1; // copy constructor called.

    test obj3(obj2); // again copy constructor called.

    obj1 = obj2; // overloaded assignment operator function.

    return 0;
}

输出:

Default Ctor called
Copy Ctor called
Copy Ctor called
Overload operator function called

在您的情况下,ostream的拷贝构造函数在两种情况下都被调用。

看起来我当时非常正确:D。感谢清晰的解释。节日快乐! - Jeffrey Vandenborne

4

6
你应该在这篇文章中提供答案。当其他人需要回答这个问题时,链接可能已经失效了。如果你想得到声望,你应该发布实际的答案。这是Stack Overflow社区的常规礼貌。 - jalf
轻微改写自第二个链接:ostream os = &fb; 是复制初始化,而 os 总是使用 ostream 的复制构造函数进行初始化。 (“=” 只是 C 语言的语法保留;永远不会调用 operator=。)编译器实际上允许(但不是必须)在这种情况下优化掉复制构造。如果它确实进行了优化,则仍然必须可以访问复制构造函数。指南: 最好使用形式 ostream os(&fb)。 它在任何 ostream os = &fb 可用的地方都有效,并具有其他优点(例如,它可以接受多个参数)。 - Mike D.
在这种情况下,当然 ostream os = &fb; 会失败,因为流无法复制。不知道为什么问题没有提到这一点。 - Johannes Schaub - litb

1
函数调用初始化的一个重要好处是它们也适用于需要多个参数的构造函数。例如,fstream构造函数可以接受两个参数:
std::fstream file("filename", ios_base::out);

C++0x统一初始化广泛普及之前,函数调用初始化是处理多参数构造函数的唯一方法。


0
据我理解,&var 是 var 变量的别名,无论使用哪个都没有关系。
--------补充-----------------
下面的代码摘自 Stroustrup 的书。从这里可以清楚地看出两者都是同一变量的别名。它还如下所述。
“参数传递的语义被定义为初始化的语义,因此当调用时,increment 的参数 aa 成为 x 的另一个名称。” 这就是为什么我将 &x 称为 x 的别名。
void increment(int& aa) { aa++; }
void f() { int x = 1; increment(x); }

不,&varvar 的地址。它们有不同的类型;如果 var 是类型 T,那么 &var 就是类型 T*。 - R Samuel Klatchko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接