构造函数中的这个怪异的结肠成员(“:”)语法是什么?

发布于 2025-01-26 17:20:03 字数 377 浏览 2 评论 0原文

最近,我看到了以下示例:

#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 ++书中永远不会找到吗?

Recently I've seen an example like the following:

#include <iostream>

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

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

What does this strange : bar(num) mean? It somehow seems to initialize the data member, but I've never seen this syntax before. It looks like a function/constructor call but for an int. This makes no sense to me.

Are there any other esoteric language features like this, you'll never find in an ordinary C++ book?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

我不是你的备胎 2025-02-02 17:20:03
Foo(int num): bar(num)    

该构造在C ++中称为成员初始化器列表

简而言之,它初始化您的成员bar to value num


构造函数内的初始化和分配有什么区别?

成员初始化:

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

成员分配:

Foo(int num)
{
   bar = num;
}

使用成员初始化器列表初始化成员和在构造体主体内部分配值之间存在显着差异。

当您 初始化 通过成员初始化器列表列表将调用一次构造函数,并且将在一个操作中构造和初始化对象。

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

如您所见,还有一个额外的创作开销&amp;后者的作业,对于用户定义的类可能很大。

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类成员,但没有实际的开销。


您什么时候必须使用成员初始化列表?

您将有(而不是强制)使用成员初始化器列表,如果

  • 您的类有参考成员
  • ,您的类具有非静态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没有默认的构造函数,因此要初始化其成员,需要使用成员初始化器列表。

使用成员初始化器列表时要注意的要点:

类成员变量始终以班级声明的顺序初始化。

它们是 note 在它们在成员初始化列表中指定的。
简而言之,成员初始化列表不能确定初始化的顺序。

鉴于以上始终是一个好习惯,即保持成员初始化的成员初始化的顺序与班级定义中声明的顺序相同。这是因为编译器不会警告两个订单是否不同,但是相对较新的用户可能会将成员初始化器列表作为初始化的顺序混淆,并编写一些与此有关的代码。

Foo(int num): bar(num)    

This construct is called a Member Initializer List in C++.

Simply said, it initializes your member bar to a value num.


What is the difference between Initializing and Assignment inside a constructor?

Member Initialization:

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

Member Assignment:

Foo(int num)
{
   bar = num;
}

There is a significant difference between Initializing a member using Member initializer list and assigning it an value inside the constructor body.

When you initialize fields via Member initializer list the constructors will be called once and the object will be constructed and initialized in one operation.

If you use assignment then the fields will be first initialized with default constructors and then reassigned (via assignment operator) with actual values.

As you see there is an additional overhead of creation & assignment in the latter, which might be considerable for user defined classes.

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

The latter is actually equivalent to:

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

While the former is equivalent to just:

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

For an inbuilt (your code example) or POD class members there is no practical overhead.


When do you HAVE TO use Member Initializer list?

You will have(rather forced) to use a Member Initializer list if:

  • Your class has a reference member
  • Your class has a non static const member or
  • Your class member doesn't have a default constructor or
  • For initialization of base class members or
  • When constructor’s parameter name is same as data member(this is not really a MUST)

A code example:

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 doesn't have a default constructor so it has to be initialized through member initializer list.
  • Base class MyClass does not have a default constructor, So to initialize its member one will need to use Member Initializer List.

Important points to Note while using Member Initializer Lists:

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

They are not initialized in the order in which they are specified in the Member Initializer List.
In short, Member initialization list does not determine the order of initialization.

Given the above it is always a good practice to maintain the same order of members for Member initialization as the order in which they are declared in the class definition. This is because compilers do not warn if the two orders are different but a relatively new user might confuse member Initializer list as the order of initialization and write some code dependent on that.

滥情哥ㄟ 2025-02-02 17:20:03

它是成员初始化列表。您应该在任何好C ++书籍中找到有关它的信息。

在大多数情况下,您应该在成员初始化列表中初始化所有成员对象((但是,请注意,在常见问题解答条目末尾列出的例外)。

常见问题解答条目的要点是,

所有其他内容均等,如果您使用初始化列表而不是分配,则您的代码将更快地运行。

It's a member initialization list. You should find information about it in any good C++ book.

You should, in most cases, initialize all member objects in the member initialization list (however, do note the exceptions listed at the end of the FAQ entry).

The takeaway point from the FAQ entry is that,

All other things being equal, your code will run faster if you use initialization lists rather than assignment.

伪装你 2025-02-02 17:20:03

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

考虑以下两个示例:

// 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

这全都与效率有关。

That's constructor initialisation. It is the correct way to initialise members in a class constructor, as it prevents the default constructor being invoked.

Consider these two examples:

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

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

In example 1:

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

In example 2:

Bar bar(b) // copy constructor

It's all about efficiency.

把昨日还给我 2025-02-02 17:20:03

这称为初始化列表。这是一种初始化班级成员的方式。使用它是有好处的,而不是简单地为构造函数的主体中的成员分配新值,但是如果您的类成员是 startants 参考>必须初始化

This is called an initialization list. It is a way of initializing class members. There are benefits to using this instead of simply assigning new values to the members in the body of the constructor, but if you have class members which are constants or references they must be initialized.

满天都是小星星 2025-02-02 17:20:03

这并不晦涩,是 c ++初始化列表styntax

基本上,在您的情况下,在您的情况下,在您的情况下,x将用_xy _yz with 代码> _z

This is not obscure, it's the C++ initialization list syntax

Basically, in your case, x will be initialized with _x, y with _y, z with _z.

凶凌 2025-02-02 17:20:03

另一个已经向您解释说,您观察到的语法称为“构造函数初始化器列表”。该语法使您可以自定义类的基础子对象和类的成员子对象(而不是允许它们默认initialize或保持非初始化)。

我只想注意到,正如您所说,“看起来像构造函数调用”的语法不一定是构造函数调用。在C ++语言中,()语法只是初始化语法的一种标准形式。对于不同类型的解释方式有所不同。对于具有用户定义的构造函数的类类型,这意味着一件事(确实是构造函数调用),对于没有用户定义的构造函数的类类型,这意味着另一件事(so so称为 value初始化 )formeet ())和对于非类型类型,这再次意味着不同的东西(因为非类型没有构造函数)。

在您的情况下,数据成员具有类型intint不是类型,因此它没有构造函数。对于类型int,本语法简单地表示“初始化bar具有num的值”,仅此而已。它是这样的,直接直接涉及构造函数,因为int再次不是类型的类型,因此它不能具有任何构造函数。

The other already explained to you that the syntax that you observe is called "constructor initializer list". This syntax lets you to custom-initialize base subobjects and member subobjects of the class (as opposed to allowing them to default-initialize or to remain uninitialized).

I just want to note that the syntax that, as you said, "looks like a constructor call", is not necessarily a constructor call. In C++ language the () syntax is just one standard form of initialization syntax. It is interpreted differently for different types. For class types with user-defined constructor it means one thing (it is indeed a constructor call), for class types without user-defined constructor it means another thing (so called value initialization ) for empty ()) and for non-class types it again means something different (since non-class types have no constructors).

In your case the data member has type int. int is not a class type, so it has no constructor. For type int this syntax means simply "initialize bar with the value of num" and that's it. It is done just like that, directly, no constructors involved, since, once again, int is not a class type of therefore it can't have any constructors.

半山落雨半山空 2025-02-02 17:20:03

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

I don't know how you could miss this one, it's pretty basic. That's the syntax for initializing member variables or base class constructors. It works for plain old data types as well as class objects.

心如狂蝶 2025-02-02 17:20:03

这是一个初始化列表。它将在运行构造函数之前初始化成员。
在第一个示例中考虑

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”。

This is an initialization list. It'll initialize the members before the constructor body is run.
Consider

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

vs

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

In the first example, str will be initialized by its no-argument constructor

string();

before the body of the Foo constructor. Inside the foo constructor, the

string& operator=( const string& s );

will be called on 'str' as you do str = p;

Wheras in the second example, str will be initialized directly by
calling its constructor

string( const string& s );

with 'p' as an argument.

滥情哥ㄟ 2025-02-02 17:20:03

您是正确的,这确实是初始化成员变量的一种方式。除了清楚地表明这是一个初始化之外,我不确定这是否有很多好处。在代码中具有“ bar = num”,可以更轻松地移动,删除或误解。

You are correct, this is indeed a way to initialize member variables. I'm not sure that there's much benefit to this, other than clearly expressing that it's an initialization. Having a "bar=num" inside the code could get moved around, deleted, or misinterpreted much more easily.

淡看悲欢离合 2025-02-02 17:20:03

还有另一个“好处”

如果成员变量类型不支持null初始化或它的参考(不能null初始化),那么您别无选择,只能提供初始化列表,

there is another 'benefit'

if the member variable type does not support null initialization or if its a reference (which cannot be null initialized) then you have no choice but to supply an initialization list

古镇旧梦 2025-02-02 17:20:03

这是构造函数的初始化列表。而不是默认构造xyz,然后分配它们在参数中收到的值,而是将这些成员用这些值初始化放下蝙蝠。对于float s来说,这似乎并不是很有用,但是它的定制类构造昂贵,这可能是一个延时的时间。

It's an initialization list for the constructor. Instead of default constructing x, y and z and then assigning them the values received in the parameters, those members will be initialized with those values right off the bat. This may not seem terribly useful for floats, but it can be quite a timesaver with custom classes that are expensive to construct.

柠檬心 2025-02-02 17:20:03

该线程尚未提及:由于C ++ 11,成员初始化器列表可以使用列表限制(又称“统一初始化”,“ Brad Initialition”):

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

它具有与其他上下文中列表initialization相同的语义。

Not mentioned yet on this thread: since C++11, the member initializer list can use list-initialization (aka. "uniform initialization", "braced initialization"):

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

which has the same semantics as list-initialization in other contexts.

思念满溢 2025-02-02 17:20:03

尽管这是一个古老的讨论,但我找不到有关委托构造师,使用以下方式使用怪异的“:”符号。

class Foo 
{
public: 
    Foo(char x, int y) 
    {}
    Foo(int y) : Foo('a', y) 
    {}
};

它的作用只是简单地将foo(y)授予foo('a',y)。因此,

Foo foo(15); //=> foo('a', 15)

在定义委派构造函数时,除了目标构造函数以外,您在初始化器列表中不能有任何成员。

Although this is an old discussion, I couldn't find any mention about delegating constructor, which uses the weird ":" symbol in the following way.

class Foo 
{
public: 
    Foo(char x, int y) 
    {}
    Foo(int y) : Foo('a', y) 
    {}
};

What it does is simply delegating Foo(y) into Foo('a', y). So that

Foo foo(15); //=> foo('a', 15)

When defining a delegating constructor, you cannot have any members in initializer list besides targeted constructor.

祁梦 2025-02-02 17:20:03

类构造函数中的结肠语法(:)是什么?将整数传递到std :: vector&lt;&gt; constructor do的是什么?

我想从

class UnionFind {
    public:
        UnionFind(int sz) : root(sz) {
            for (int i = 0; i < sz; i++) {
                root[i] = i;
            }
        }
    private:
        vector<int> root;
    };
    
    
    int main() {
        
       
        UnionFind uf(10);
    }

。表示“初始化列表”的开始,或“ noreferrer”>“ initializer list” ,,,,,将每个变量初始化为括号中的值。好像您正在调用每个变量的构造函数,而该值将传递给该变量的构造函数。

因此,:root(sz)root变量以int sz初始化,就像做vector&lt; int&gt; root(sz);。但是,这样做允许sz可以传递到unionfind类构造函数。

在此处初始化具有类似大小的向量的构造函数#3( https:// https:/// en.cppreference.com/w/cpp/container/vector/Vector/Vector ):

// Constructor (3) as shown at
// https://en.cppreference.com/w/cpp/container/vector/vector
explicit vector( size_type count,
                 const T& value = T(),
                 const Allocator& alloc = Allocator());

它构造countSZ在上面的示例中)向量,每个都带有值t(),在这种情况下为int() /代码>。 int()看起来像一个函数调用,但基本上是一个整数默认构造函数值零(0)。它称为“值初始化” 。如果整数是对象并且具有构造函数,则可以将其视为称为“整数构造函数”。另请参阅我的问题: to char> char()是C ++中的函数?

因此,让我们回顾一下::构造函数中的root(sz)就像构造vector&lt; int&gt; root(sz);,它创建sz root vector中的元素数,每个元素都具有初始值int(int> int(),这是零。

还要注意,count传递给构造函数#3的参数确实应该是size_type,可以写为std :: vector :: size_type ,通常为size_t(请参阅“成员类型”部分在这里)。因此,最好将int sz更改为size_t sz。因此,将此构造函数线更改:unionFind(int sz):root(sz){ this:unionfind(size_t sz):root(sz){

What is the colon syntax (:) in the class constructor and what does passing an integer to a std::vector<> constructor do?

I'd like to explain the below example from this duplicate question:

class UnionFind {
    public:
        UnionFind(int sz) : root(sz) {
            for (int i = 0; i < sz; i++) {
                root[i] = i;
            }
        }
    private:
        vector<int> root;
    };
    
    
    int main() {
        
       
        UnionFind uf(10);
    }

The colon (:) signifies the start of an "initialization list", or "initializer list", which initializes each variable to the value in parenthesis. It is as though you were calling a constructor for each variable with that value in parenthesis being passed in to that variable's constructor.

So, : root(sz) initializes the root variable with an int sz, which is like doing vector<int> root(sz);. Doing it like this, however, allows sz to get passed in to the UnionFind class constructor.

Initializing a vector with a size like that is constructor #3 here (https://en.cppreference.com/w/cpp/container/vector/vector):

// Constructor (3) as shown at
// https://en.cppreference.com/w/cpp/container/vector/vector
explicit vector( size_type count,
                 const T& value = T(),
                 const Allocator& alloc = Allocator());

It constructs count (sz in the example above) number of elements into the vector, each with value T(), which means int() in this case since root is a vector<int>. int() looks like a function call but is basically an integer default constructor to value zero (0). It is called "value initialization". Think of it like calling an "integer constructor", if integers were objects and had constructors. See also my question here: What is a call to char() as a function in C++?

So, let's recap: : root(sz) in the constructor is like constructing vector<int> root(sz);, which creates sz number of elements in the root vector, each with initial value int(), which is the syntax for "value initialization" of an int to zero.

Note also that the count parameter passed to constructor #3 shown above really should be a size_type, which can be written as std::vector::size_type, and is usually size_t (see the "Member types" section here). So, it would be better to change int sz to size_t sz instead. Therefore, change this constructor line: UnionFind(int sz) : root(sz) { to this instead: UnionFind(size_t sz) : root(sz) {.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文