施工与初始化形式差异

发布于 2025-02-10 15:15:19 字数 2458 浏览 2 评论 0原文

我正在学习C ++使用列出的书籍在这里。现在,我遇到了C ++底漆的以下声明:

当我们分配内存块时,我们经常计划在其中构建对象 根据需要的记忆。在这种情况下,我们想将内存分配从对象分配 构造。

将初始化与分配结合通常是我们想要的 分配一个对象。在这种情况下,我们几乎可以肯定知道对象的价值 应该有。

(强调我的)

这里要注意的重要一件事是,C ++底漆似乎表明构造与初始化相同,并且它们与分配不同,这对我来说很有意义。

请注意,我刚刚引用了完整段落中的选定部分,以保持讨论简洁并说明我的观点。如果需要在这里a>。


现在,我从 class.dtor

对于具有非平凡构造函数的对象,在构造函数开始执行之前,指对象的任何非静态成员或基类,导致不确定的行为。对于具有非平凡驱动器的对象,指代损坏器完成执行后的任何非静态成员或对象的基类都导致不确定的行为。

(重点是我的)

现在标准是否确切指定何时(什么时候)构造函数执行开始?

以给您更多的上下文,请考虑

class A {
    public:
        A(int)
        {
     
        } 
};

class B : public A {
    int j;
    public:
         int f()
        {
          return 4;
        }
//------v-----------------> #2
        B() : A(f()),
//------------^-----------> #3
              j(f())
//------------^-----------> #4
        { //<---------------#5
         
        }
};
int main()
{
    B b; #1
    return 0;
}

  1. ​B()根据标准开始执行?请注意,我知道a(f())是未定义的行为。做b :: b()在点#1#2#3,<代码>#4 或#5。换句话说,标准是否准确指定何时(什么时候)构造函数执行开始?

  2. 在此给定的示例中,构造和初始化是否相同。我的意思是我理解,在成员初始化列表中,我们的j(f()),我们正在初始化数据成员j,但此初始化也意味着构造b :: b()已经开始在点#4

    开始执行

我在最近的一个so post point #4上开始,因此该帖子开始似乎也暗示了初始化和构造是相同的。


在问这个问题之前,我读了很多帖子,但是我无法提出一个根据C ++标准正确的答案。

我还阅读 this ,这似乎表明分配,初始化和构造都是不同的:

  1. 分配
    这是为对象分配内存的步骤。
  2. 初始化
    这是与语言相关的对象属性“设置”的步骤。
  3. 施工
    现在分配和初始化对象,正在执行构造函数。是否使用默认构造函数取决于对象的创建方式。

如上所述,用户声称所有提到的术语都是不同的,而不是C ++引物的建议。因此,根据标准,C ++底漆(称构造和初始化是相同)或上述引用的答案(构造和初始化是不同的),此处的主张是正确的

I am learning C++ using the books listed here. Now I came across the following statement from C++ Primer:

When we allocate a block of memory, we often plan to construct objects in that
memory as needed. In this case, we’d like to decouple memory allocation from object
construction.

Combining initialization with allocation is usually what we want when we
allocate a single object. In that case, we almost certainly know the value the object
should have.

(emphasis mine)

The important thing to note here is that C++ primer seems to suggest that construction is the same as initialization and that they are different from allocation which makes sense to me.

Note that I've just quoted selected parts from the complete paragraph to keep the discussion concise and get my point across. You can read the complete para if you want here.


Now, I came across the following statement from class.dtor:

For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

(emphasis mine)

Now does the standard specifies exactly when(at what point) the constructor execution begins?

To give you some more context consider the example:

class A {
    public:
        A(int)
        {
     
        } 
};

class B : public A {
    int j;
    public:
         int f()
        {
          return 4;
        }
//------v-----------------> #2
        B() : A(f()),
//------------^-----------> #3
              j(f())
//------------^-----------> #4
        { //<---------------#5
         
        }
};
int main()
{
    B b; #1
    return 0;
}

My questions are:

  1. At what point does the derived class' constructor B::B() start executing according to the standard? Note that I know that A(f()) is undefined behavior. Does B::B() starts executing at point #1, #2, #3, #4 or #5. In other words, does the standard specifies exactly when(at what point) the constructor execution begins?

  2. Is construction and initialization the same in this given example. I mean I understand that in the member initializer list where we have j(f()), we're initializing data member j but does this initialization also implies that the construction B::B() has begun executing at point #4?

I read in a recent SO post that execution of derived ctor begins at point #4 and so that post also seem to suggest that Initialisation and construction is the same.


I read many posts before asking this question but I wasn't able to come up with an answer that is right according to the C++ standard.

I also read this which seems to suggest that allocation, initialization and construction are all different:

  1. Allocation
    This is the step where memory is allocated for the object.
  2. Initialization
    This is the step where the language related object properties are "set". The vTable and any other "language implementation" related operations are done.
  3. Construction
    Now that an object is allocated and initialized, the constructor is being executed. Whether the default constructor is used or not depends on how the object was created.

As you can see above, the user claims that all of the mentioned terms are different as opposed to what is suggested by C++ primer. So which claim is correct here according to the standard, C++ Primer(which says that construction and Initialisation is same) or the above quoted quoted answer(what says that construction and Initialisation are different).

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

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

发布评论

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

评论(2

草莓酥 2025-02-17 15:15:19

初始化和构造有些相似,但不一样。

对于类型的对象,初始化是通过调用构造函数来完成的(例如,有例外,例如,汇总初始化不使用构造函数;在调用构造函数之前,成员在成员中使用构造函数)。

非级类型没有构造函数,因此如果没有它们,它们将被初始化。

您的C ++引物报价使用“初始化”和“构造”来指代同一件事。将其称为“初始化”是更正确的,不要将自己限制在类型中,并包括构造函数调用以外的其他初始化的其他部分。


该标准是否确切指定何时(什么时候)构造函数执行开始?

是的。 b b;是默认的initialization,对于类,调用默认构造函数,什么也没做。

因此,默认构建器b()是这里执行的第一件事。

如上所述在这里,,b( )调用a()在评估其参数f()之后,然后初始化j在评估其初始器f( )。最后,它执行自己的身体,在您的情况下是空的。

由于正文是最后执行的,因此认为b()本身是在a()和/或初始化j 。


我在最近的一篇文章中阅读了派生的CTOR的执行,从Point#4

开始

您还应该阅读我的评论,以挑战该声明。

Initialization and construction are somewhat similar, but not the same.

For objects of class types, initialization is done by calling a constructor (there are exceptions, e.g. aggregate initialization doesn't use a constructor; and value-initialization zeros the members in before calling a constructor).

Non-class types don't have constructors, so they are initialized without them.

Your C++ Primer quote uses "initialization" and "construction" to refer to the same thing. It would be more correct to call it "initialization" to not limit yourself to class types, and to include other parts of initialization other than a constructor call.


does the standard specifies exactly when(at what point) the constructor execution begins?

Yes. B b; is default-initialization, which, for classes, calls the default constructor and does nothing else.

So the default-constructor B() is the first thing that's executed here.

As described here, B() calls A() after evaluating its argument f(), then initializes j after evaluating its initializer f(). Finally, it executes its own body, which is empty in your case.

Since the body is executed last, it's a common misunderstanding to think that B() itself is executed after A() and/or after initializing j.


I read in a recent SO post that execution of derived ctor begins at point #4

You should also read my comments to that post, challenging this statement.

萤火眠眠 2025-02-17 15:15:19

在您的示例中,点1和2之间没有区别。这是同一点。当对象实例化时,将对象的构造函数被调用。无论是点1还是第2点,这都是非物质的。

您要在此处分开的是对象的基础内存的分配(点1)以及新对象的构造函数开始执行(点2)。

但这是一个没有区别的区别。这些不是C ++中的两个离散事件,可以以某种方式作为离散的,单独的步骤进行。它们是不可分割的,它们是一个又一个。您不能为C ++对象分配内存,但避免以某种方式构建它。同样,您也不能不首先分配内存而构造某些对象。这是同一件事。

现在,您确实会在这里发生其他干扰,例如使用安置新操作员的服务(并在某个时候手动调用对象的灾难)。这似乎表明分配是分开的,但实际上并非如此。位置的调用新操作员有效地隐含地分配了新对象的内存 您将指针移交给位置的新操作员,然后立即遵循通过对象的结构。那就是思考的方式。因此,分配+结构仍然是不可分割的步骤。

要理解的另一个重要点是,每个对象的构造函数要做的第一件事是将其源自所有对象的构造函数调用。因此,从实际方面来看, 实际上 发生在显示的代码中的第一件事是,a的构造方法首先被称为然后,一旦构建它,一旦完成,就进行b的“真实”构造。

B() : A(f()),

这正是这所读的。 b的构造器开始执行,其第一阶业务是调用a的构造函数。 B什么都不做,直到a的构造函数完成。因此,从技术上讲,b的构造器开始先执行,然后a的构造函数。但是,b的构造器无能为力,直到a的构造函数处理其业务。

b构造器所做的第一件事是调用a的构造函数。这是在C ++标准中指定的。

B() : j(f()), A(f())

如果您尝试这样做,您的编译器将对您大喊大叫。

因此,总结一下:当您实例化对象,任何对象时,发生的第一件事就是调用其构造函数。就是这样。故事的结尾。这里的每一个可能的变化(放置新的,没有构造函数的吊舱,带有虚拟调度的附加歌曲和舞蹈例程)都在下游,可以定义为特殊的构造函数的特殊和特定的实现。

There is no distinction between point 1 and 2 in your example. It's the same point. An object's constructor gets invoked when the object gets instantiated, when it comes into existence. Whether it's point 1 or point 2, that's immaterial.

What you are separating out here is the allocation of the underlying memory for the object (point 1) and when the new object's constructor begins executing (point 2).

But that's a distinction without a difference. These are not two discrete events in C++ that somehow can be carried out as discrete, separate steps. They are indivisible, they are one and the same. You cannot allocate memory for a C++ object but somehow avoid constructing it. Similarly you cannot construct some object without allocating memory for it first. It's the same thing.

Now, you do have other distractions that can happen here, like employing the services of the placement-new operator (and manually invoking the object's destructor at some point later down the road). That seems to suggest that allocation is something that's separate, but its really not. The invocation of the placement new operator is, effectively, implicitly allocating the memory for the new object from the pointer you hand over to the placement new operator, then this is immediately followed by the object's construction. That's the way to think about it. So allocation+construction is still an indivisible step.

Another important point to understand is that the first thing that every object's constructor does is call the constructors of all objects that it's derived from. So, from a practical aspect, the very first thing that actually happens in the shown code is that A's constructor gets called first, to construct it, then, once its construction is finished, then B's "real" construction takes place.

B() : A(f()),

That's exactly what this reads. B's constructor starts executing, and its first order of business is to call A's constructor. B does nothing else, until A's constructor finishes. So, technically, B's constructor starts executing first, then A's constructor. But, B's constructor does nothing until A's constructor handles its business.

The first thing that B's constructor does is call A's constructor. This is specified in the C++ standard.

B() : j(f()), A(f())

If you try to do this your compiler will yell at you.

So, to summarize: when you instantiate an object, any object, the very first thing that happens is its constructor gets called. That's it. End of story. Every possible variation here (placement new, PODs with no constructors, the additional song-and-dance routine with virtual dispatch) is all downstream of this, and can be defined as special, and specific, implementations of constructors.

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