C++构造函数问题

发布于 2024-10-17 10:11:44 字数 542 浏览 5 评论 0原文

C++编程绝对初学者,第二版书中,有以下陈述:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

这是否等于:

HeapPoint::HeapPoint(int x, int y) { thePoint = new Point(x,y); }

并且,由于我们在构造函数中执行此操作,为分配的值是多少>xy?我们应该在 new Point(x,y) 中写入由 xy 代替的值吗?或者说,这样是正确的吗?

更新:我想我得到了初始化xy的想法,因为在书中它在函数中具有以下内容:

HeapPoint myHeapPoint(2,4);

In the C++ programming for the absolute Beginner, 2nd edition book, there was the following statement:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

Is this equal to:

HeapPoint::HeapPoint(int x, int y) { thePoint = new Point(x,y); }

And, since we are doing this in a constructor, what are the values assigned for x and y? Should we write values insted of x and y in new Point(x,y)? Or, it is correct that way?

UPDATE: I think I got the idea of initializing x and y, as in the book it has the following in a function:

HeapPoint myHeapPoint(2,4);

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

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

发布评论

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

评论(6

岁月如刀 2024-10-24 10:11:45

一般来说,您应该更喜欢第一个构造,即使用初始化列表。

如果

  1. 您希望在它周围放置一个 try..catch ,或者
  2. 您有几个这样的构造并将它们存储为常规指针,那么第二种构造是更可取的,在这种情况下,您需要注意您的新闻之一可能会失败,并且您将需要做一些清理工作。您可以使用某种 auto_ptr / unique_ptr 来处理这个问题,直到您知道所有分配都已成功,然后释放它们。这是因为您可能在析构函数中删除它们,但如果构造函数抛出异常,则析构函数将不会被调用。

Generally you should prefer the first construct, i.e. using the initialiser list.

The second construct is preferable if

  1. you wish to put a try..catch around it or
  2. You have several of these and are going to store them as regular pointers, in which case you need to beware that one of your news might fail and you will need to do some cleanup. You would handle this by using some kind of auto_ptr / unique_ptr until you know all the allocations have succeeded, then go around releasing them. This is because you presumably delete them in your destructor, but your destructor will not get called if the constructor throws.
淡淡離愁欲言轉身 2024-10-24 10:11:45

假设 thePoint 是一个原始指针,无论其意图和目的如何,它都是相同的。第一个版本初始化 thePoint,而第二个版本则对其进行赋值,但效果几乎总是相同的,甚至生成完全相同的机器代码。

他们什么时候不一样了?

  1. 如果 thePoint 是某种智能指针,第一种形式将通过将新的 Point 传递给其构造函数来初始化它,而第二种形式可能会将其零初始化,然后将新的 Point 传递给其赋值操作员。
  2. thePoint是多个成员变量之一时,第一种形式将按照它在类定义中出现的顺序对其进行初始化,而第二种形式将在构造函数的主体中进行赋值。因此,两种形式之间“水合”的顺序可能有所不同,如果某些成员变量依赖于其他成员变量,这可能很重要。

如果您是初学者,其中大部分内容可能对您来说毫无意义。但不用担心。这些差异非常微妙,几乎永远不会产生影响。 (我确信还有其他例外,但现在还没有想到。)

这最终是风格和一致性的问题,为此我更喜欢初始化形式而不是赋值形式。

Assuming thePoint is a raw pointer, it is, for all intents and purposes, the same. The first version initialises thePoint, whereas the second assigns to it, but the effect is almost always the same, even to the point of generating exactly the same machine code.

When aren't they the same?

  1. If thePoint is some kind of smart pointer, the first form will initialise it by passing the new Point to its constructor, whereas the second form will probably zero-initialise it and then pass the new Point to its assignment operator.
  2. When thePoint is one of several member variables, the first form will initialise it in the order it appears in the class definition, whereas the second form will assign in the body of the constructor. So the order in which things get "hydrated" may vary between the two forms, which may matter if some members variables depend on others.

If you are a beginner, most of this is probably meaningless to you. But don't worry about it. These differences are very subtle and will almost never matter. (I'm sure there are other exceptions, but none come to mind just now.)

It's ultimately a matter of style and consistency, for which I prefer the initialisation form over the assignment form.

绿萝 2024-10-24 10:11:45

这两种形式并不完全相同,因为初始化列表给出了一个直接放入变量中的值,而变量则在适当的情况下默认初始化(例如 std::strings 默认为空,但 int、double等(成员变量具有有效的随机值),然后在遇到赋值操作时被覆盖。这种覆盖可能效率较低,但对于某些在创建后不允许更改的常量和引用成员变量来说也可能是非法的。一般来说,只要有可能就使用初始化列表,这样就不会出错。只有当您发现由于需要先执行其他一些步骤而无法初始化某些内容时,才应该在构造函数体内放置显式赋值语句。请注意,初始化按照成员变量在类中声明的顺序进行,无论您在构造函数本身中列出它们的顺序如何:好的编译器(例如 GCC)可以对此发出警告。

The two forms aren't quite the same in that the initialisation list gives a value to be placed directly into the variable, whereas the variables are otherwise default-initialised if appropriate (e.g. std::strings are empty by default, but int, double etc. member variables have effectively random values), then overwritten when the assignment operation is encountered. This overwriting can be less efficient, but it can also be illegal for some constant and reference member variables that aren't allowed to be changed after they're created. In general, use the initialiser list whenever possible and you won't go far wrong. Only when you find you can't initialise something yet because you need to do some other steps first should you put an explicit assignment statement inside the constructor body. Be aware the initialisations take place in the order in which the member variables are declared in the class, irrespective of the order you list them in the constructor itself: a good compiler (e.g. GCC) can warn you about this.

走过海棠暮 2024-10-24 10:11:45

第一个与第二个不一样。

在这种特定情况下,它们可能会产生相同的结果。 但是,Point可以轻松地为new Point实现赋值运算符并做一些“不同”的事情(我没有这本书,所以我不知道每一个细节)。同样,赋值运算符应该做你所期望的......但是,thePoint可能是一个容器(例如,智能指针),它可以(由于某些奇怪的原因) ) 使用初始化(Point)默认初始化后赋值 时的行为有所不同。

在这种情况下,这些细节可能并不重要,但它们确实会影响初始化顺序和执行。当您的程序增长时,这种差异将变得很重要。那时,初始化需要时间,并且您需要确保对象正确初始化:它们被正确构造(第一次)并且它们以正确的顺序构造。最明显的情况:当默认构造函数的行为与带参数的构造函数不同时,特别是当构造函数产生分配或具有其他耗时(或行为不同)的副作用时,它将产生影响。

而且,由于我们是在构造函数中执行此操作,因此为 int x 和 int y 分配的值是多少?

这完全取决于 Point 的构造函数。

我们应该在 new Point(x,y) 中写入 x 和 y 的值吗?或者说,这样是正确的吗?

首选方法(对于大多数团队来说)是尽可能使用初始化列表和正式构造函数,并编写类型以支持正确的初始化。当代码库增长时,会出现很多微妙之处。此构造函数使用初始化列表:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

如果您愿意,在假设的情况下可能需要正确的初始化像这样声明 thePoint:

const Point* const thePoint;

第一个 const 意味着您不能修改 Point(例如,Point.xPoint.y)。第二个 const 意味着您不能为该变量分配新的分配。 OP 中的示例是简单的示例,但随着程序的增长绝对有帮助。

the first is not the same as the second.

in this specific case, they will probably produce the same result. however, Point could easily implement an assignment operator for new Point and do something 'different' (i don't have the book, so i don't know every detail). as well, the assignment operator should do what you'd expect... however, thePoint could be a container (e.g., smart pointer) which could (for some odd reason) behave differently when using initialization(Point) vs default initialization followed by assignment.

these details likely won't matter in this case, but they do affect initialization order, and the execution. the difference will be important when your programs grow. at that time, initialization will take time, and you will want to ensure that the objects are initialized correctly: that they are constructed properly (the first time) and that they are constructed in the right order. most obvious case: it will make a difference when a default constructor behaves different from one with parameters, especially when the constructor produces allocations or has other time-consuming (or behaviorally different) side effects.

And, since we are doing this in a constructor, what are the values assigned for int x and int y?

that depends entirely on Point's constructor.

Should we write values insted of x and y in new Point(x,y)? Or, it is correct that way?

the preferred way (for most teams) is to use the initialization list and formal constructors wherever possible, and to write your types to support correct initialization. there are a lot of subtleties that come out when codebases grow. this constructor uses the initialization list:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

proper initialization could in a hypothetical case be required if you want to declare thePoint like so:

const Point* const thePoint;

the first const means that you cannot modify the Point (e.g., Point.x or Point.y). the second const means that you cannot assign a new allocation to the variable. trivial examples for the example in the OP, but definitely helpful as your programs grow.

暮倦 2024-10-24 10:11:45

在第一种情况下,您使用初始化列表来设置成员thePoint,而在第二个示例中,您使用构造函数的主体。后面完成的工作是不一样的,因为初始化列表是在构建对象之前使用的,一旦完成,就会使用构造函数主体。

因此:

  • 直接使用thePoint(new Point(x,y))

    构建的

  • 在第二种情况下,它可能是第一个分配的因为它有一个默认的构造函数,然后构建的对象将被主体中的调用覆盖(是的,相同的一个,但不在同一位置)!!所以你在这里失去了效率。

如果存在初始化列表,这是有充分理由的(C++ 是一种非常精确的语言,语法非常严格,除了来自 C 的丑陋的东西之外,一切都是合乎逻辑的)!例如,如果您的类使用了引用成员,则您必须使用初始化列表,因为您的对象不会完全构建:

class A
{
public:
    B& _b;
    C& _c;

    A(B& b, C& c):_b(b), _c(c) // compiles !
    {

    }

    A(B& b, C& c)
    {
        _b(b); // does not compile !
        _c(c); // does not compile !
    }
}

请记住,这里,如果我们完成了 _c(c), _b( b) 在第一个构造函数中(相反顺序),类 BC 的复制构造函数无论如何都会以相同的顺序调用:它们被称为 按照定义成员的顺序(即_b_c之前),而不是编写它们的顺序!!!!

In your 1st case, you use an initialization list to setup your member thePoint, whereas in your 2nd example you use the body of the constructor. The work done behind is not the same, since initialization lists are used before your object is built, and once this is finished your constructor body is used.

So:

  • in your 1st case, your member thePoint is directly built with thePoint(new Point(x,y))

  • in the 2nd case it is first allocated probably because a default constructor is available for it, then the object built is overridden with the call made in the body (yes, the same one, but not at the same place) !! So you lost efficiency here.

If initialization lists exist, this is for good reasons (C++ is a very precise language, the syntax is really strict, except for ugly things coming from C all is logical) ! For instance if your class used a reference member, you'd be obliged to use an initialization list, since your object would not be entirely built:

class A
{
public:
    B& _b;
    C& _c;

    A(B& b, C& c):_b(b), _c(c) // compiles !
    {

    }

    A(B& b, C& c)
    {
        _b(b); // does not compile !
        _c(c); // does not compile !
    }
}

Keep in mind that here, if we had done _c(c), _b(b) in the 1st contructor (reverse order), copy constructors of classes B and C would be called in the same order anyway: they are called in the order the members are defined (i.e. _b before _c), not the order you write them !!!!

他是夢罘是命 2024-10-24 10:11:45

两者都是一样的。第一种情况:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

使用thePoint所属类的构造函数,并使其指向为Point新分配的内存。

第二种情况也是分配内存,其地址分配给thePoint

Both are same. The first case:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

uses the constructor of the class to which thePoint belongs, and makes it point to a new allocated memory for Point.

The second case also assigns the memory, and its address is assigned to thePoint

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