11

我正在阅读这个 C++ 开源代码,我来到了一个构造函数,但我不明白(基本上是因为我不懂 C++ :P)

我非常了解 C 和 Java。

 TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
     _someMethod( 0 ),
     _someOtherMethod( 0 ),
     _someOtherOtherMethod( 0 ),
     _someMethodX( 0 ) 
  {
       int bla;
       int bla;
  }

据我所知,第一行只声明了构造函数的名称,“::”对我来说听起来像是“属于”的。{} 之间的代码是它自己的构造函数体。

我“认为”参数和第一个“{”之后的内容就像方法默认参数或其他东西,但我在网上找不到合理的解释。我在示例中发现的大多数 C++ 构造函数与 Java 中的构造函数几乎相同。

我的假设是对的吗?"::" 就像是属于,params 和 body 之后的列表就像 "default args" 什么的?

更新: 感谢您的回答。这些可以称为方法吗?(我猜不是)在构造函数体内调用它们有什么区别

4

8 回答 8

20

最常见的情况是这样的:

class foo{
private:
    int x;
    int y;
public:
    foo(int _x, int _y) : x(_x), y(_y) {}
}

这将设置x并设置为在构造函数参数y中给出的值_x_y这通常是构造任何声明为数据成员的对象的最佳方式。

您也可能正在查看构造函数链接:

class foo : public bar{
    foo(int x, int y) : bar(x, y) {}
};

在这种情况下,类的构造函数将调用其基类的构造函数并传递值xy

进一步剖析函数:

TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
   _someMethod( 0 ),
   _someOtherMethod( 0 ),
   _someOtherOtherMethod( 0 ),
   _someMethodX( 0 ) 
{
     int bla;
     int bla;
}

-::运算符称为范围解析运算符。它基本上只是表明它TransparentObject是 的成员TransparentObject。其次,假设构造函数的主体出现在花括号中是正确的。

更新:感谢您的回答。这些可以称为方法吗?(我猜不是)在构造函数体内调用它们有什么区别

关于这个主题的信息比我在这里给你的要多得多。您必须使用初始化器列表的最常见区域是当您初始化引用或 a 时const,因为这些变量必须在创建时立即被赋予一个值。

于 2008-10-16T23:28:00.510 回答
10

你很接近。第一行是声明。:: 左边的标签是类名,要成为构造函数,函数名必须与类名相同。

TransparentObject::TransparentObject( int w, int x, int y, int z )

在 C++ 中,您可以选择在函数体开始之前放置一个冒号和一些成员变量的初始值。如果您正在初始化任何const变量或将参数传递给超类构造函数,则必须使用此技术。

: 
 _someMethod( 0 ),
 _someOtherMethod( 0 ),
 _someOtherOtherMethod( 0 ),
 _someMethodX( 0 )

然后是大括号中的构造函数的主体。

{
   int bla;
   int bla;
}
于 2008-10-16T23:30:02.410 回答
7

:: 实际上意味着包含(请参阅注释以进行澄清),但是 _someMethods 等等就是所谓的初始化列表。链接上有很多信息=]

编辑:对不起,我的第一句话不正确 - 请参阅评论。

于 2008-10-16T23:26:55.597 回答
2

是的, :: 是 C++ 范围运算符,它可以让您告诉编译器函数属于什么。在构造函数声明之后使用 : 开始所谓的初始化列表。

于 2008-10-16T23:27:14.490 回答
2

参数列表和{}s 之间的代码指定(某些)类成员的初始化。

初始化而不是赋值——它们是不同的东西——所以这些都是对构造函数的调用。

于 2008-10-16T23:27:45.050 回答
1

你是对的。它是一种为类变量设置默认值的方法。我不太熟悉将它们放在 : 之后和函数体中的确切区别。

于 2008-10-16T23:27:01.980 回答
1

使用初始化列表通常有一些很好的理由。一方面,您不能设置在构造函数的初始化列表之外引用的成员变量。此外,如果成员变量需要为其自己的构造函数提供某些参数,则必须在此处传递它们。比较一下:

class A
{
public:
  A();
private:
  B _b;
  C& _c;
};

A::A( C& someC )
{
  _c = someC; // this is illegal and won't compile. _c has to be initialized before we get inside the braces
  _b = B(NULL, 5, "hello"); // this is not illegal, but B might not have a default constructor or could have a very 
                            // expensive construction that shouldn't be done more than once
}

到这个版本:

A::A( C& someC )
: _b(NULL, 5, "hello") // ok, initializing _b by passing these arguments to its constructor
, _c( someC ) // this reference to some instance of C is correctly initialized now
{}
于 2008-11-30T17:13:20.140 回答
0

在不使用初始化列表的情况下,所有类成员都将简单地调用其默认构造函数,因此这是您可以控制调用哪个构造函数的唯一位置(对于非动态分配的成员)。调用哪个父类构造函数也是如此。

类成员在构造函数的主体中“初始化”(即在 {} 大括号之间使用 = 运算符)在技术上不是初始化,而是一个赋值。对于具有非平凡构造函数/析构函数的类,默认构造然后以这种方式通过赋值进行修改可能会很昂贵。对于引用成员,您必须使用初始化列表,因为它们不能通过赋值运算符进行更改。

如果成员(或父类)没有默认构造函数,则未能在初始化列表中指定适当的构造函数将导致编译器生成错误。否则编译器将插入默认构造函数调用本身。对于内置类型,这没有任何作用,因此您将在那里有垃圾值。

请注意,在初始化程序列表中指定成员的顺序不会影响调用它们的顺序。它总是首先是父类构造函数(如果有的话),然后是类成员(按照它们在类定义中定义的顺序)。将它们放在初始化列表中的顺序无关紧要,并且可能是细微错误的来源......

在下面的人为示例中,看起来意图是m_bvaluethen m_awith初始化m_b,但实际发生的是m_a用 初始化m_b(它本身尚未初始化)然后m_b用 初始化valuem_b只会包含垃圾!

struct BadInitialiserListExample
{
    BadInitialiserListExample(int value) :
        m_b(value),
        m_a(m_b)      // <-- *BUG* this is actually executed first due to ordering below!
    {
    }

    int    m_a;
    int    m_b;
};
于 2011-11-12T13:02:22.387 回答