在绝对初学者,第2版书的 C ++编程中,有以下声明:
HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }
这等于:
HeapPoint::HeapPoint(int x, int y) { thePoint = new Point(x,y); }
而且,既然我们是在构造函数中执行此操作,那么为x
和y
分配的值是什么?我们应该在x
中写下y
和new Point(x,y)
的内容吗?或者,这是正确的吗?
更新:我认为我已初步了解x
和y
,因为它在函数中有以下内容:
HeapPoint myHeapPoint(2,4);
答案 0 :(得分:4)
通常,您应该更喜欢第一个构造,即使用初始化列表。
如果
,第二种结构更可取答案 1 :(得分:2)
假设thePoint
是一个原始指针,它可以用于所有意图和目的。第一个版本初始化thePoint
,而第二个版本分配给它,但效果几乎总是相同,甚至达到生成完全相同的机器代码的程度。
什么时候不一样?
thePoint
是某种智能指针,第一个表单将通过将新Point传递给它的构造函数来初始化它,而第二个表单可能会将其初始化为零,然后将新Point传递给它的赋值操作者。thePoint
是几个成员变量之一时,第一个表单将按照它在类定义中出现的顺序初始化,而第二个表单将在构造函数的主体中赋值。因此,事物得到“补充”的顺序可能会因两种形式而异,如果某些成员变量依赖于其他形式,这可能很重要。如果您是初学者,大部分内容对您来说可能毫无意义。但不要担心。这些差异非常微妙,几乎无关紧要。 (我确定还有其他例外情况,但刚才没有想到。)
这最终是风格和一致性的问题,我更喜欢初始化表格而不是作业表格。
答案 2 :(得分:1)
这两种形式并不完全相同,因为初始化列表给出了一个直接放入变量的值,而变量在其他情况下默认初始化(例如std :: strings默认为空,但是int,double等成员变量具有有效的随机值),然后在遇到赋值操作时被覆盖。这种覆盖效率可能较低,但对于某些常量和引用成员变量在创建后不允许更改也可能是非法的。通常,尽可能使用初始化列表,您不会出错。只有当你发现你还不能初始化某些东西因为你需要先做其他一些步骤时,你应该在构造函数体内放置一个显式的赋值语句。请注意,初始化按成员变量在类中声明的顺序发生,而不管您在构造函数本身中列出它们的顺序:一个好的编译器(例如GCC)可以向您发出警告。
答案 3 :(得分:0)
第一个与第二个不同。
在这种特定情况下,他们可能会产生相同的结果。 然而,Point 可以轻松实现new Point
的赋值运算符并执行“不同”的操作(我没有这本书,所以我不知道每个细节)。同样,赋值运算符应执行您期望的操作...但是, 可能是一个容器(例如,智能指针),可能(由于一些奇怪的原因) )使用initialization(Point)
vs default initialization followed by assignment
时行为不同。
这些细节在这种情况下可能无关紧要,但它们确实会影响初始化顺序和执行。当你的程序增长时,差异将是很重要的。那时,初始化将花费时间,并且您将需要确保正确初始化对象:它们是正确构造的(第一次)并且它们以正确的顺序构造。最明显的情况:当默认构造函数与带参数的构造函数不同时,它会有所不同,特别是当构造函数产生分配或具有其他耗时(或行为不同)的副作用时。
而且,既然我们在构造函数中执行此操作,那么为int x和int y分配的值是什么?
完全取决于Point
的构造函数。
我们应该在新的Point(x,y)中写出x和y的值吗?或者,这是正确的吗?
首选方法(对大多数团队而言)是尽可能使用初始化列表和正式构造函数,并编写类型以支持正确的初始化。代码库增长时会出现很多细微之处。此构造函数使用初始化列表:
HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }
const Point* const thePoint;
第一个const
表示您无法修改该点(例如,Point.x
或Point.y
)。第二个const意味着您无法为变量分配新的分配。 OP中示例的简单示例,但在程序增长时绝对有用。
答案 4 :(得分:0)
两者都是一样的。第一种情况:
HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }
使用thePoint
所属的类的构造函数,并使其指向Point
的新分配内存。
第二种情况也分配内存,其地址分配给thePoint
答案 5 :(得分:0)
在第一种情况下,您使用初始化列表来设置您的成员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
),而不是你编写它们的顺序!