5

C++ Programming for the absolute Beginner, 2nd edition book 中,有以下语句:

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

这是否等于:

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

而且,由于我们在构造函数中执行此操作,为x和分配的值是什么y?我们应该写 insted ofxyin的值new Point(x,y)吗?或者,这样是正确的?

更新:我想我得到了初始化xand的想法y,正如书中它在函数中具有以下内容:

HeapPoint myHeapPoint(2,4);
4

6 回答 6

4

通常你应该更喜欢第一个构造,即使用初始化列表。

第二种结构更可取,如果

  1. 你想尝试一下..catch 或者
  2. 您有其中几个,并将它们存储为常规指针,在这种情况下,您需要注意您的一个新闻可能会失败,您需要进行一些清理。你可以通过使用某种 auto_ptr / unique_ptr 来处理这个问题,直到你知道所有的分配都成功了,然后去释放它们。这是因为您可能在析构函数中删除了它们,但如果构造函数抛出,您的析构函数将不会被调用。
于 2011-02-14T10:02:51.473 回答
2

假设thePoint是一个原始指针,对于所有意图和目的,它都是相同的。第一个版本初始化thePoint,而第二个分配给它,但效果几乎总是相同的,甚至生成完全相同的机器代码。

什么时候不一样?

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

如果您是初学者,那么大部分内容对您来说可能毫无意义。但不要担心。这些差异非常微妙,几乎不会有任何影响。(我敢肯定还有其他例外,但现在没有想到。)

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

于 2011-02-14T09:48:50.793 回答
1

这两种形式并不完全相同,因为初始化列表给出了一个直接放入变量中的值,而变量在适当的情况下被默认初始化(例如 std::strings 默认为空,但 int、double等成员变量具有有效的随机值),然后在遇到赋值操作时被覆盖。这种覆盖可能会降低效率,但对于某些在创建后不允许更改的常量和引用成员变量也可能是非法的。一般来说,尽可能使用初始化列表,这样就不会出错。只有当你发现你还不能初始化一些东西,因为你需要先做一些其他的步骤时,你才应该在构造函数体中放置一个显式的赋值语句。

于 2011-02-14T10:04:58.870 回答
0

第一个与第二个不同。

在这种特定情况下,它们可能会产生相同的结果。然而,Point可以很容易地实现一个赋值运算符new Point并做一些“不同”的事情(我没有这本书,所以我不知道每一个细节)。同样,赋值运算符应该做你所期望的......但是, thePoint可能是一个容器(例如,智能指针),它可能(出于某种奇怪的原因)在使用initialization(Point)vs时表现不同default initialization followed by assignment

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

而且,由于我们在构造函数中执行此操作,为 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.xPoint.y)。第二个 const 意味着您不能为变量分配新的分配。OP 中示例的琐碎示例,但随着程序的增长绝对有帮助。

于 2011-02-14T10:00:17.140 回答
0

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

所以:

  • 在您的第一种情况下,您的成员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)在第一个构造函数中完成(反向顺序),则复制类的构造函数,B并且C无论如何都会以相同的顺序调用:它们按照定义成员的顺序(即_b之前_c)调用,而不是命令写!!!!

于 2011-02-14T10:18:39.897 回答
0

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

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

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

第二种情况也分配了内存,它的地址分配给thePoint

于 2011-02-14T10:03:52.837 回答