我什么时候必须使用初始化列表来初始化 C++班级成员?

发布于 2024-08-09 06:42:18 字数 562 浏览 2 评论 0原文

假设我有 std::map< std::string,std::string > m_someMap 作为类 A 的私有成员变量

两个问题:(我问的唯一原因是因为我遇到了这样的代码)

  1. 这一行的目的是什么:

    A::A() : m_someMap()
    

    现在我知道这是初始化,但是你必须这样做吗? 我很困惑。

  2. std::map<的默认值是多少std::string,std::string > m_someMap,C# 还定义 int、double 等始终初始化为默认 0,对象初始化为 null(至少在大多数情况下) 那么C++中的规则是什么?对象是否默认初始化为 null,原语初始化为垃圾? 当然,我正在考虑实例变量。

编辑:

另外,由于大多数人指出这是一种样式选择并且不是必需的,那么:

A::A() : m_someMap(), m_someint(0), m_somebool(false)

let's say I have
std::map< std::string, std::string > m_someMap as a private member variable of class A

Two questions: (and the only reason I'm asking is because I came across code like that)

  1. What's the purpose of this line:

    A::A() : m_someMap()
    

    Now I know that this is intialization, but do you have to do this like that?
    I'm confused.

  2. What's the default value of std::map< std::string, std::string > m_someMap, also C# defines that int, double, etc. is always initialized to defualt 0 and objects are to null (at least in most cases)
    So what's the rule in C++?? are object initialized by defualt to null and primitives to garbage?
    Of course I'm taking about instance variables.

EDIT:

also, since most people pointed out that this is a style choice and not necessary, what about:

A::A() : m_someMap(), m_someint(0), m_somebool(false)

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

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

发布评论

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

评论(7

国际总奸 2024-08-16 06:42:18

m_somemap

  1. 你不必这样做。
  2. 如果省略它,你会得到什么:一个空的 std::map< std::string, std::string >,即该映射的有效实例,其中没有元素。

m_somebool

  1. 如果您希望它具有已知值,则必须将其初始化为 truefalse。布尔值是“普通的旧数据类型”,它们没有构造函数的概念。此外,C++ 语言没有为非显式初始化的布尔值指定默认值。
  2. 如果省略它,您会得到什么:具有未指定值的布尔成员。您不能这样做并稍后使用它的值。因此,强烈建议您初始化该类型的所有值。

m_someint

  1. 如果您希望它具有已知值,则必须将其初始化为某个整数值。整数是“普通的旧数据类型”,它们没有构造函数的概念。此外,C++ 语言没有为非显式初始化的整数指定默认值。
  2. 如果省略它,您会得到什么:一个具有未指定值的 int 成员。您不能这样做并稍后使用它的值。因此,强烈建议您初始化该类型的所有值。

m_somemap

  1. You don't have to.
  2. What you get if you omit it: An empty std::map< std::string, std::string >, i.e., a valid instance of that map which has no elements in it.

m_somebool

  1. You have to initialize it to true or false if you want it to have a known value. Booleans are "plain old data types" and they do not have the concept of a constructor. Moreover, the C++ language does not specify default values for non-explicitly-initialized booleans.
  2. What you get if you omit it: a boolean member with an unspecified value. You must not do this and later use its value. Because of that, it is a strongly recommended policy that you initialize all values of this type.

m_someint

  1. You have to initialize it to some integer value if you want it to have a known value. Integers are "plain old data types" and they do not have the concept of a constructor. Moreover, the C++ language does not specify default values for non-explicitly-initialized integers.
  2. What you get if you omit it: an int member with an unspecified value. You must not do this and later use its value. Because of that, it is a strongly recommended policy that you initialize all values of this type.
往昔成烟 2024-08-16 06:42:18

没有必要实际去做。
默认构造函数会自动执行此操作。

但有时通过使其显式化,它可以充当某种文档:

class X
{
    std::map<string,string>  data;
    Y                        somePropertyOfdata;

    X()
      :data()                    // Technically not needed
      ,somePropertyOfdata(data)  // But it documents that data is finished construction
    {}                           // before it is used here.
};

C++ 中的规则是,除非显式初始化 POD 数据,否则它是未定义的,而其他类则具有自动调用的默认构造函数(即使程序员没有显式这样做)。

但这么说。考虑一下:

template<typename T>
class Z
{
     T  data;   
     Z()
        :data()    // Technicall not need as default constructor will
                   // always be called for classes.
                   // But doing this will initialize POD data correctly
                   // if T is a basic POD type. 
     {}
};

在这里您希望数据默认初始化。
从技术上讲,POD 没有构造函数,所以如果 T 是 int 那么你会期望它做任何事情吗?因为它是显式初始化的,所以它被设置为 0 或 POD 类型的等效值。

对于编辑:

class A
{
    std::map<string,string>   m_someMap;
    int                       m_someint;
    bool                      m_somebool;
   public:
    A::A()
       : m_someMap()      // Class will always be initialised (so optional)
       , m_someint(0)     // without this POD will be undefined
       , m_somebool(false)// without this POD will be undefined
    {}
};

There is no need to actually do it.
The default constructor will autmatically do it.

But somtimes by making it explicit it acts as sort of documentation:

class X
{
    std::map<string,string>  data;
    Y                        somePropertyOfdata;

    X()
      :data()                    // Technically not needed
      ,somePropertyOfdata(data)  // But it documents that data is finished construction
    {}                           // before it is used here.
};

The rule in C++ is that unless you explicitly initialise POD data it is undefined while other classes have there default constructor called automatically (even if not explicitly done so by the programmer).

But saying that. Consider this:

template<typename T>
class Z
{
     T  data;   
     Z()
        :data()    // Technicall not need as default constructor will
                   // always be called for classes.
                   // But doing this will initialize POD data correctly
                   // if T is a basic POD type. 
     {}
};

Here you would exepect data to be default initialized.
Technically POD do not have constructors so if T was int then would you expect it to do anything? Becuase it was explicitly initialize it is set to 0 or the equivalent for POD types.

For the edit:

class A
{
    std::map<string,string>   m_someMap;
    int                       m_someint;
    bool                      m_somebool;
   public:
    A::A()
       : m_someMap()      // Class will always be initialised (so optional)
       , m_someint(0)     // without this POD will be undefined
       , m_somebool(false)// without this POD will be undefined
    {}
};
旧情勿念 2024-08-16 06:42:18

正如其他人指出的:这没有必要,但或多或​​少是风格问题。
好处:它表明您明确想要使用默认构造函数并使您的代码更加冗长。缺点:如果您有多个 ctor,那么维护所有这些 ctor 的更改可能会很痛苦,有时您添加类成员却忘记将它们添加到 ctor 初始值设定项列表中,从而使其看起来不一致。

As others pointed out: it is not necessary but more or less a matter of style.
The upside: it shows that you explicitly want to use the default constructor and makes your code more verbose. The downside: If you have more than one ctor it can be a pain to maintain changes in all of them and sometimes you add class members and forget to add them to the ctors initializer list and make it look inconsistent.

女皇必胜 2024-08-16 06:42:18
A::A() : m_someMap()

在这种情况下,这条线是不必要的。然而,一般,这是初始化类成员的唯一正确方法。

如果您有这样的构造函数:

X() : y(z) {
 w = 42;
}

那么当调用 X 构造函数时,会发生以下情况:

  • 首先,所有成员都被初始化:对于 y,我们明确表示我们希望调用以 z 作为参数的构造函数。对于 w,发生的情况取决于 w 的类型。如果 w 是 POD 类型(也就是说,基本上是 C 兼容类型:没有继承,没有构造函数或析构函数,所有成员都是 public,并且所有成员也是 POD 类型),那么它是 < em>未初始化。它的初始值是在该内存地址发现的任何垃圾。如果 w 是非 POD 类型,则调用其默认构造函数(非 POD 类型始终在构造时初始化)。
  • 一旦两个成员都被构造完毕,我们然后调用赋值运算符将 42 赋给 w

需要注意的重要一点是,所有构造函数都是在我们进入构造函数主体之前调用的。一旦我们进入主体,所有成员都已经被初始化。
所以我们的构造函数体可能存在两个问题。

  • 如果 w 属于没有默认构造函数的类型怎么办?那么这将无法编译。那么它必须:之后显式初始化,就像y一样。
  • 如果调用默认构造函数和赋值运算符的这个顺序不必要地慢怎么办?也许一开始就简单地调用正确的构造函数会更有效。

简而言之,由于 m_someMap 是非 POD 类型,严格来说我们不需要执行 : m_someMap() 。无论如何,它都会被默认构建。但如果它是 POD 类型,或者如果我们想调用默认构造函数之外的另一个构造函数,那么我们就需要这样做。

A::A() : m_someMap()

This line is unnecessary in this case. However, in general, it is the only proper way to initialize class members.

If you have a constructor such as this:

X() : y(z) {
 w = 42;
}

then the following happens when the X constructor is called:

  • First, all members are initialized: for y, we explicitly say we wish to call the constructor which takes a z as its argument. For w, what happens depends on the type of w. If w is a POD type (that is, basically a C-compatible type: No inheritance, no constructors or destructors, all members public, and all members are POD types as well), then it is not initialized. Its initial value is whatever garbage was found at that memory address. If w is a non-POD type, then its default constructor is called (non-POD types are always initialized on construction).
  • Once both members have been constructed, we then call the assignment operator to assign 42 to w.

The important thing to note is that all constructors are called before we enter the body of the constructor. Once we're in the body, all members have already been initialized.
So there are two possible problems with our constructor body.

  • What if w is of a type that doesn't have a default constructor? Then this won't compile. Then it must be explicitly initialized after the :, like y is.
  • What if this sequence of calling both default constructor and assignment operator is needlessly slow? Perhaps it'd be much more efficient to simply call the correct constructor to begin with.

So in short, since m_someMap is a non-POD type, we don't strictly speaking need to do : m_someMap(). It would have been default constructed anyway. But if it had been a POD type, or if we had wanted to call another constructor than the default one, then we would have needed to do this.

浮生未歇 2024-08-16 06:42:18

只是为了清楚发生了什么(关于你的第二个问题)

std::map< std::string,std::string > m_someMap 创建一个名为 m_someMap 的堆栈变量,并在其上调用默认构造函数。 C++ 对于所有对象的规则是:

T varName;

其中 T 是类型,varName 是默认构造的。

T* varName;

应显式分配为 NULL(或 0)——或新标准中的 nullptr。

Just to be clear about what's happening (with regards to your 2nd question)

std::map< std::string, std::string > m_someMap creates a stack variable called m_someMap and the default constructor is called on it. The rule for C++ for all of your objects is if you go:

T varName;

where T is a type, varName is default constructed.

T* varName;

should be explicitly assigned to NULL (or 0) -- or nullptr in the new standard.

独自唱情﹋歌 2024-08-16 06:42:18

澄清默认值问题:

C++ 没有某些类型隐式引用的概念。除非某些东西被显式声明为指针,否则它不能采用空值。这意味着每个类都会有一个默认构造函数,用于在未指定构造函数参数时构建初始值。如果没有声明默认构造函数,编译器将为您生成一个。此外,每当类包含类类型的成员时,这些成员将在对象构造时通过它们自己的默认构造函数隐式初始化,除非您使用冒号语法显式调用不同的构造函数。

碰巧的是,所有 STL 容器类型的默认构造函数都会构建一个空容器。其他类可能对其默认构造函数的功能有其他约定,因此您仍然需要知道它们在这种情况下被调用。这就是为什么 A::A() : m_someMap() 行实际上只是告诉编译器执行它已经执行的操作。

To clarify the default value issue:

C++ does not have the concept of some types implicity being by-reference. Unless something is explicitly declared as a pointer, it cannot ever take a null value. This means that every class will have a default constructor for building the initial value when no constructor parameters are specified. If no default constructor is declared, the compiler will generate one for you. Also, whenever a class contains members that are of classed types, those members will be implicitly initialized via their own default constructors at object construction, unless you use the colon syntax to explicitly call a different constructor.

It just so happens that the default constructor for all STL container types builds an empty container. Other classes may have other conventions for what their default constructors do, so you still want to be aware that they're being invoked in situations like this. That's why the A::A() : m_someMap() line, which is really just telling the compiler to do what it would do already anyway.

甜扑 2024-08-16 06:42:18

在C++中创建一个对象时,构造函数会经历以下顺序:

  1. 调用整个类树中所有父虚类的构造函数(顺序任意)
  2. 按声明顺序调用所有直接继承父类的构造函数
  3. Call按声明顺序的所有成员变量的构造函数

还有一些比这更具体的内容,并且某些编译器允许您强制执行某些特定顺序的操作,但这是一般想法。对于每个构造函数调用,您都可以指定构造函数参数,在这种情况下,C++ 将调用指定的构造函数,或者您也可以不理会它,C++ 将尝试调用默认构造函数。默认构造函数就是不带参数的构造函数。

如果任何虚拟父类、非虚拟父类或成员变量没有默认构造函数,或者需要使用默认构造函数以外的其他内容创建,则可以将它们添加到构造函数调用列表中。因为 C++ 假定默认构造函数调用,所以将默认构造函数放入列表中和完全将其排除在外之间绝对没有区别(C++ 不会(除非在本问题范围之外的特殊情况下)创建一个对象而不调用某种构造函数)。如果一个类没有默认构造函数并且您没有提供构造函数调用,则编译器将抛出错误。

当涉及到 floatint 等内置类型时,默认构造函数根本不执行任何操作,因此变量将具有片段中剩余内容的默认值的记忆。所有内置类型也都有一个复制构造函数,因此您可以通过将它们的初始值作为唯一参数传递给变量的构造函数来初始化它们。

When you create an object in C++, the constructor goes through the following sequence:

  1. Call the constructors of all parent virtual classes in the entire class tree (In an arbitrary order)
  2. Call the constructors of all directly inherited parent classes in the order of declaration
  3. Call the constructors of all member variables in the order of declaration

There are a few more specifics than this, and some compilers allow you to force a few things out of this specific order, but this is the general idea. For each of these constructor calls you can specify the constructor arguments, in which case C++ will call the constructor as specified, or you can leave it alone and C++ will try to call the default constructor. The default constructor is simply the one that takes no arguments.

If any of your virtual parent classes, non-virtual parent classes, or member variables don't have a default constructor or need to be created with something other than the default, you add them to the list of constructor calls. Because C++ assumes a default constructor call, there is absolutely no difference between putting a default constructor in the list and leaving it out entirely (C++ will not (unless under special circumstances outside the scope of this question) create an object without a call to a constructor of some sort). If a class does not have a default constructor and you do not provide a constructor call, the compiler will throw an error.

When it comes to built in types such float or int, the default constructor does nothing at all, and so the variable will have the default value of whatever was left in the piece of memory. All built in types also have a copy constructor so you can you can initialize them by passing their initial value as the only argument to the variable's constructor.

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