在初始化列表中初始化和在构造函数

时间:2015-09-14 07:48:32

标签: c++

我不确定我是否正确地提出了我的问题,所以请随时纠正我。我相信:

  1. 初始化列表中的初始化相当于
    int a = a;

  2. 在构造函数中初始化相当于
    int a; a = a;

  3. 但我仍然无法弄清楚以下输出的原因:

    #include <iostream>
    using namespace std;
    
    class test
    {
        int a,b;
        public:
    
        /*
        test(int a, int b): a(a), b(b) {}         //OUTPUT: 3 2
        test(int a, int b) {   a = a; b = b;}     //OUTPUT: -2 1972965730
        test(int c, int d) {   a = c; b = d;}     //OUTPUT: 3 2 
        Hence it does work without this pointer. Unless the variable names are same
        */
    
        void print()    {   cout<<a<<" "<<b<<"\n";}
    };
    
    int main()
    {
        test A(3,2);
        A.print();
        return 0;
    }
    

    编辑:

    1. 正如M.M所指出的:等同于a(a)的是this->a = a

    2. 值得一读:Why should I prefer to use member initialization list?

    3. 有两种解决方法:

      test(int a, int b) {   this->a = a; this->b = b;}
      test(int a, int b) {   test::a = a; test::b = b;}
      

4 个答案:

答案 0 :(得分:9)

test(int a, int b) {   a = a; b = b;}

这不正确。它对数据成员没有任何作用。它应该是

test(int a, int b) {   this->a = a; this->b = b;}

答案 1 :(得分:2)

在初始化列表中,语法是这样的:parens ()之外的每个变量名都是类成员。 parens里面的内容是在范围内发生的任何事情 - 无论是类成员还是构造函数参数。参数将隐藏类成员

所以你可以安全地做到:

class MyClass
{
    int i;
    MyClass(int i): i(i) {}
    //              ^-must be class member
};

编译器将正确使用 中的参数来初始化parens之外的类成员

内部发生的事情parens的范围与构造函数体中发生的范围相同。

所以:

class MyClass
{
    int i;
    MyClass(int i)
    {
        i = i; // BOTH of those are the parameter i
    }
}

名为i参数隐藏名为i类成员,因此类成员永远不会在构造函数体中访问。

您必须使用this明确歧义

class MyClass
{
    int i;
    MyClass(int i)
    {
        // now we set class member i to parameter i
        this->i = i; 
    }
}

所有这些都是在初始化列表的语法中为您完成的:

    //                v-this parameter is hiding the class member
    MyClass(int i): i(i) {}
    //              ^-must be the class member

初始化列表基本上是:this->i = i为你做。

如果可能,您应该始终初始化初始化列表中的成员。

答案 2 :(得分:0)

尝试更换 test(int a, int b) { a = a; b = b;} test(int a, int b) { this->a = a; this->b = b;} expect(['foo', 'bar']).to.include.members(['foo', 'bar']) 我的编译器(msvc)产生了所需的结果。

答案 3 :(得分:0)

每个功能都引入了一个新范围。将构造函数定义为

test(int a, int b) { a = a; b = b; }

你隐藏了班级成员。编译器无法知道左a属于类,右a是参数。

将构造函数声明为

 test(int c, int d) { a = c; b = d; }

你没有遇到这个问题,因为没有更多的命名冲突。

在建议的修复中,同样的推理适用:

test(int a, int b) { this->a = a; this->b = b; }

通过使用a指针明确限定lhs this是一个类成员,该指针隐式传递给每个成员函数,包括构造函数。但是,此代码不等同于使用初始化列表进行初始化。你的问题是对的:

  
      
  1. 在初始化列表中初始化相当于   int a = a;
  2.   
  3. 在构造函数中初始化等效于   int a; a = a;
  4.   

如果你的成员变量是一些复杂的类,这将产生很大的不同。如果没有初始化列表,您将首先使用默认构造函数创建一个对象,然后为其分配一个新值,而如果使用初始化列表,则只会发生复制构造。

因此,您应该总是更喜欢使用初始化列表:

test(int a, int b): a(a), b(b) {}