首页 文章

构造函数中这个奇怪的冒号成员(“:”)语法是什么?

提问于
浏览
280

最近我见过如下例子:

#include <iostream>

class Foo {
public:
  int bar;
  Foo(int num): bar(num) {};
};

int main(void) {
  std::cout << Foo(42).bar << std::endl;
  return 0;
}

这奇怪的 : bar(num) 是什么意思?它似乎初始化成员变量,但我以前从未见过这种语法 . 它看起来像一个函数/构造函数调用,但对于 int ?对我没有任何意义 . 也许有人可以启发我 . 而且,顺便说一下,还有其他任何类似的深奥语言功能,你永远不会在一本普通的C书中找到它吗?

12 回答

  • 5

    这不是模糊的,它是C++ initialization list syntax

    基本上,在您的情况下, x 将初始化 _xy_yz_z .

  • 267

    这是一个成员初始化列表 . 您应该在任何good C++ book中找到有关它的信息 .

    You should, in most cases, initialize all member objects in the member initialization list(但是,请注意FAQ条目末尾列出的例外情况) .

    FAQ条目的主要内容是,

    在所有其他条件相同的情况下,如果使用初始化列表而不是赋值,则代码将运行得更快 .

  • 6
    Foo(int num): bar(num)
    

    此构造在C中称为 Member Initializer List .

    简单地说,它 initializes 你的成员 bar 到一个值 num .


    构造函数中的Initializing和Assignment有什么区别?

    Member Initialization:

    Foo(int num): bar(num) {};
    

    Member Assignment:

    Foo(int num)
    {
       bar = num;
    }
    

    使用成员初始值设定项列表初始化成员并在构造函数体内为其指定值之间存在显着差异 .

    当您通过成员初始化程序列表 initialize 字段时,构造函数将被调用一次,并且将在一次操作中构造和初始化该对象 .

    如果使用 assignment ,则首先使用默认构造函数初始化字段,然后使用实际值重新分配(通过赋值运算符) .

    如您所见,在后者中存在创建和分配的额外开销,这对于用户定义的类可能是相当大的 .

    Cost of Member Initialization = Object Construction 
    Cost of Member Assignment = Object Construction + Assignment
    

    后者实际上相当于:

    Foo(int num) : bar() {bar = num;}
    

    而前者相当于:

    Foo(int num): bar(num){}
    

    对于内置(您的代码示例)或POD类成员,没有实际开销 .


    您何时需要使用会员初始化列表?

    如果出现以下情况,您将 have(rather forced) to 使用会员初始化程序列表:

    • 您的 class 有一名参考会员

    • 您的类有一个非静态const成员或

    • 您的类成员没有默认构造函数或

    • 用于初始化基类成员或

    • 当构造函数的参数名称与数据成员相同时(这实际上不是必须的)


    代码示例:

    class MyClass
    {
        public:
            //Reference member, has to be Initialized in Member Initializer List
            int &i;       
            int b;
            //Non static const member, must be Initialized in Member Initializer List
            const int k;  
    
        //Constructor’s parameter name b is same as class data member 
        //Other way is to use this->b to refer to data member
        MyClass(int a, int b, int c):i(a),b(b),k(c)
        {
             //Without Member Initializer
             //this->b = b;
        }
    };
    
    class MyClass2:public MyClass
    {
        public:
            int p;
            int q;
            MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
            {
            }
    
    };
    
    int main()
    {
        int x = 10;
        int y = 20;
        int z = 30;
        MyClass obj(x,y,z);
    
        int l = 40;
        int m = 50;
        MyClass2 obj2(x,y,z,l,m);
    
        return 0;
    }
    
    • MyClass2 没有默认构造函数,因此必须通过成员初始化列表初始化它 .

    • 基类 MyClass 没有默认构造函数,因此要初始化其成员,需要使用Member Initializer List .


    使用会员初始化列表时需要注意的要点:

    Class Member variables are always initialized in the order in which they are declared in the class.

    它们按照在成员初始化列表中指定的顺序进行初始化 not .
    简而言之,成员初始化列表不确定初始化的顺序 .

    鉴于上述情况,保持成员初始化的成员顺序与在类定义中声明它们的顺序始终是一个好习惯 . 这是因为如果两个订单不同,编译器不会发出警告,但是相对较新的用户可能会将成员初始化程序列表混淆为初始化顺序,并编写一些依赖于此的代码 .

  • 5

    这是构造函数的初始化 . 这是在类构造函数中初始化成员的正确方法,因为它可以防止调用默认构造函数 .

    考虑这两个例子:

    // Example 1
    Foo(Bar b)
    {
       bar = b;
    }
    
    // Example 2
    Foo(Bar b)
       : bar(b)
    {
    }
    

    在示例1中:

    Bar bar();  // default constructor
    bar = b;  // assignment
    

    在示例2中:

    Bar bar(b) // copy constructor
    

    这完全取决于效率 .

  • 1

    这称为初始化列表 . 它是初始化类成员的另一种方法 . 使用它有一些好处,而不是简单地为构造函数体中的成员赋值,但是如果你有类成员是常量或引用,则必须初始化它们 .

  • 14

    另一个已经向您解释过,您观察到的语法称为“构造函数初始化列表” . 此语法允许您自定义初始化类的基础子对象和成员子对象(而不是允许它们进行默认初始化或保持未初始化) .

    我只想注意,正如你所说,"looks like a constructor call"的语法不一定是构造函数调用 . 在C语言中, () 语法只是初始化语法的一种标准形式 . 对于不同类型,它的解释不同 . 对于具有用户定义的构造函数的类类型,它意味着一件事(它确实是构造函数调用),对于没有用户定义的构造函数的类类型,它意味着另一件事(所谓的value initialization)对于空 () )而对于非类类型它再次意味着不同的东西(因为非类型有没有建设者) .

    在您的情况下,数据成员的类型为 int . int 不是类类型,因此它没有构造函数 . 对于类型 int ,此语法仅表示“初始化 bar ,其值为 num ”,就是这样 . 就这样完成,没有涉及构造函数,因为 int 不是类类型,因此它不能有任何构造函数 .

  • 9

    我不知道你怎么会错过这个,这是非常基本的 . 这是初始化成员变量或基类构造函数的语法 . 它适用于普通的旧数据类型以及类对象 .

  • 8

    这是一个初始化列表 . 它将在构造函数体运行之前初始化成员 . 考虑

    class Foo {
     public:
       string str;
       Foo(string &p)
       {
          str = p;
       };
     };
    

    VS

    class Foo {
    public:
      string str;
      Foo(string &p): str(p) {};
    };
    

    在第一个示例中,str将由其无参数构造函数初始化

    string();
    

    在Foo构造函数的主体之前 . 在foo构造函数里面,

    string& operator=( const string& s );
    

    将在你的str = p时调用'str';

    在第二个例子中,str将通过调用其构造函数直接初始化

    string( const string& s );
    

    用'p'作为参数 .

  • 11

    你是对的,这确实是一种初始化成员变量的方法 . 我不确定这有什么好处,除了明确表示它是初始化 . 在代码中包含“bar = num”可以更容易地移动,删除或误解 .

  • 7

    还有另一个“好处”

    如果成员变量类型不支持空初始化或者它的引用(不能初始化为null)那么你别无选择,只能提供一个初始化列表

  • 174

    它是构造函数的初始化列表 . 而不是默认构造 xyz ,然后为它们分配参数中接收的值,这些成员将立即使用这些值初始化 . 这对于 float 来说似乎并不是非常有用,但是对于构造起来很昂贵的自定义类来说可能会相当长 .

  • 5

    在这个线程上还没有提到:从C 11开始,成员初始化列表可以使用列表初始化(又称“统一初始化”,“支持初始化”):

    Foo(int num): bar{num} {}
    

    它与其他上下文中的列表初始化具有相同的语义 .

相关问题