C++;构造问题 在C++初学者的第二版程序中,有以下语句: HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

C++;构造问题 在C++初学者的第二版程序中,有以下语句: HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { },c++,pointers,multiple-constructors,C++,Pointers,Multiple Constructors,这等于: HeapPoint::HeapPoint(int x, int y) { thePoint = new Point(x,y); } 既然我们在构造函数中这样做,那么为x和y分配的值是什么?我们是否应该在新点(x,y)中写入值而不是x和y?或者,这样做是正确的 更新:我想我想到了初始化x和y,因为在书中它有以下功能: HeapPoint myHeapPoint(2,4); 假设点是一个原始指针,那么无论出于何种目的,它都是相同的。第一个版本初始化点,而第二个版本为点,但效果几乎总是一

这等于:

HeapPoint::HeapPoint(int x, int y) { thePoint = new Point(x,y); }
既然我们在构造函数中这样做,那么为
x
y
分配的值是什么?我们是否应该在
新点(x,y)
中写入值而不是
x
y
?或者,这样做是正确的

更新:我想我想到了初始化
x
y
,因为在书中它有以下功能:

HeapPoint myHeapPoint(2,4);

假设
是一个原始指针,那么无论出于何种目的,它都是相同的。第一个版本初始化点,而第二个版本为点,但效果几乎总是一样的,甚至可以生成完全相同的机器代码

他们什么时候不一样了

  • 如果
    是某种智能指针,第一种形式将通过将新点传递给其构造函数来初始化它,而第二种形式可能会将其初始化为零,然后将新点传递给赋值运算符
  • 当点是多个成员变量之一时,第一种形式将按照它在类定义中出现的顺序对其进行初始化,而第二种形式将在构造函数体中赋值。因此,在这两种形式之间,物质“水合”的顺序可能会有所不同,如果某些成员变量依赖于其他成员变量,这可能会有影响
  • 如果你是一个初学者,这对你来说可能是毫无意义的。但别担心。这些差异非常微妙,几乎不重要。(我肯定还有其他例外,但现在我想不起来了。)


    归根结底,这是一个风格和一致性的问题,因此我更喜欢初始形式而不是作业形式。

    第一种形式与第二种形式不同

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

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

    既然我们在构造函数中这样做,那么为int x和int y分配的值是什么

    这完全取决于
    点的构造函数

    我们应该在新的点(x,y)中写入代替x和y的值吗?或者,这样做是正确的

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

    HeapPoint::HeapPoint(intx,inty):点(新点(x,y)){}

    如果您希望这样声明点,则在假设情况下可能需要正确初始化:

    常数点*常数点


    第一个
    const
    表示您不能修改点(例如
    Point.x
    Point.y
    )。第二个常量表示不能为变量分配新的分配。OP中的示例非常简单,但随着程序的增长肯定会有所帮助。

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

    第二种结构更可取,如果

  • 你想试一下…抓住它还是
  • 您有几个这样的消息,并将它们存储为常规指针,在这种情况下,您需要注意其中一条消息可能会失败,您需要进行一些清理。您可以使用某种自动/唯一的方法来处理这个问题,直到您知道所有分配都成功了,然后继续释放它们。这是因为您可能会在析构函数中删除它们,但如果构造函数抛出,则不会调用析构函数

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

    HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }
    
    使用点
    所属的类的构造函数,并使其指向为点
    分配的新内存


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

    这两种形式并不完全相同,因为初始化列表给出了一个直接放入变量的值,而如果合适的话,变量是默认初始化的(例如,std::字符串默认为空,但int、double等成员变量具有有效的随机值),然后在遇到赋值操作时进行覆盖。这种覆盖可能效率较低,但对于某些常量和引用成员变量来说也是非法的,这些变量在创建后不允许更改。通常,只要有可能,就使用初始化器列表,这样做不会出错。只有当您发现不要初始化某些内容,因为如果在构造函数体中放入显式赋值语句,则需要先执行一些其他步骤。请注意初始化是按顺序进行的
    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 !
        }
    }