C++ 中类的默认成员函数的问题(构造函数、析构函数、运算符 =、复制构造函数)(默认 ctor、dtor、复制 ctor)

Posted

技术标签:

【中文标题】C++ 中类的默认成员函数的问题(构造函数、析构函数、运算符 =、复制构造函数)(默认 ctor、dtor、复制 ctor)【英文标题】:Problem with default member functions of class in C++ (constructor, destructor, operator=, copy constructor) (default ctor, dtor, copy ctor) 【发布时间】:2010-04-27 14:45:27 【问题描述】:

我们知道编译器会为用户定义的类生成一些成员函数,如果这些成员函数没有定义但被使用,不是吗。所以我有这样的代码:

class AA

;

void main()

    AA a;
    AA b(a);
    a = b;

此代码运行良好。我的意思是没有编译器错误。但是下面的代码....

class AA

    int member1;
    int member2;
;

但是这段代码给出了一个运行时错误,因为变量“a”没有被初始化!!!

所以我的问题是:当我们实例化一个 int 时,它有一个值。那么为什么默认构造函数不起作用并使用这两个 int 数字初始化变量“a”??

编辑:平台:Win Vista,编译器:Visual Studio 2008 编译器;标志:默认

【问题讨论】:

第二次写:变量“a”没有被初始化!!! 可能在你的编译器集“/WX”(将警告视为错误)属性中? @Narek 这不是运行时错误。 @Bil 您的编辑虽然毫无疑问是善意的,但会使现有答案无效。我已经回滚了。 @Neil Butterworth :这可以完美地解释为由尝试读取int 对象的陷阱表示引起的运行时错误。这实际上是 VS2008(以及 VS2005)试图用这个实现的。他们用“是否初始化标志”标记变量,并在有人试图读取未初始化的值时进行陷阱。 【参考方案1】:

编译器合成的默认构造函数为所有具有构造函数的类成员调用默认构造函数。但是整数没有构造函数,因此没有初始化。但是,我很难相信这会导致运行时错误。

初始化这些变量:

class AA 
  public:
     AA() : member1(0), member2(0) 
  private:
    int member1;
    int member2;
;

【讨论】:

+1,初始化成员变量的工作量是最小的而且更干净。【参考方案2】:

首先,从实际的角度来看,这不是真正的运行时错误。这是开发环境的内置调试功能。当您读取未初始化的值时,编译器会尝试捕获情况,这正是您的情况。

其次,当我们“实例化”int 时,它没有值。更准确地说,它包含一个不确定的值,甚至不能保证它是稳定的(您可以通过连续多次读取相同的未初始化变量来获得不同的值)。从理论上讲,读取未初始化的int 变量会导致未定义的行为,因为它可能包含非法(“陷阱”)表示。事实上,您可以将开发环境生成的“运行时错误”视为未定义行为的表现。

【讨论】:

在函数调用中,未初始化的局部变量是稳定的(它们不会变化),但是每次调用函数都会重新分配局部变量,并且它们会获得恰好在堆栈内存中的任何值时间。此外,一个 int 不能包含陷阱表示,一个 int 的所有值都是有效的,你只是不知道当它未初始化时你会得到什么值。 @kk6yb:不,绝对不正确。首先,它们在一般情况下是不稳定的。语言没有做出这样的保证,实际上这种不稳定性在实践中通常是可以重现的。实际上,当变量由 CPU 寄存器表示,然后在其“值生命周期”之外访问时,即当寄存器实际用于另一个变量时,就会发生不稳定性。在一般情况下,堆栈内存与它几乎没有关系。 @kk6yb:其次,C++ 中唯一不能包含陷阱表示的类型是unsigned char(即所有位模式都是有效的)。所有其他类型,包括int 都可以包含陷阱表示。 我会接受第一点,将局部变量优化到寄存器中,它们的值可能在函数调用过程中不稳定。【参考方案3】:

什么平台?编译器?编译器标志?您必须添加一些额外的检查,因为普通 C++ 中没有任何东西检查初始化状态。

【讨论】:

【参考方案4】:

事实上,默认构造函数和复制构造函数确实有效。但在 cpp 中未初始化的变量实际上包含垃圾。因此,您会收到错误消息(int member1int member2 包含垃圾,您尝试将此垃圾分配给 b 对象)。

【讨论】:

它如何理解它是垃圾而不是我分配的值?? @Narek 一些 C++ 编译器在构建用于调试的代码时,会用一个特殊值(我认为 Visual C++ 使用 0xCDCDCDCD)填充未初始化的变量,以便检测到这一点。我的猜测是编译器生成的复制构造函数检查它正在复制的对象的字段以尝试捕获此类错误。但是,不要在发布版本中依赖它:在发布版本中,它们只会包含该内存中已经存在的任何数据。 0xCDCDCDCD 仅用于填充堆分配,而不是堆栈(局部变量)。并且具有 0xCDCDCDCD 值的 int 是有效的 (-842150451),取消引用具有 0xCDCDCDCD 的指针会陷入陷阱,但不使用 int。【参考方案5】:

首先,当你实例化一个 int 而不初始化它时,它有一个不确定的值。内置的基本类型没有构造函数。

其次,该代码不应产生运行时错误。它只是在自动生成的复制构造函数和赋值运算符中复制不确定的 int 值。它应该生成一个编译器警告,指出正在使用未初始化的变量。

第三,你的 main 签名是错误的 - 正确的签名是

int main(void)

【讨论】:

当你写 int main(void) 而不是 int main() 时,它会改变什么吗??? 根据 C++ 标准它变得正确 :) 否则,变化不大 - 但您可以使用返回值来指定成功或失败,这对于“链接”程序很有用。 Narek, int main(void); 没有区别和 int main();但你不应该使用 void main() 请问,是谁逼我这么做的??我不会毫无理由地使用一些教条的语法! C++ 标准迫使您这样做。如果您认为那是“没有理由”,也许您应该改用另一种语言。

以上是关于C++ 中类的默认成员函数的问题(构造函数、析构函数、运算符 =、复制构造函数)(默认 ctor、dtor、复制 ctor)的主要内容,如果未能解决你的问题,请参考以下文章

详解c++中类的六个默认的成员函数

详解c++中类的六个默认的成员函数

详解c++中类的六个默认的成员函数

C++类和对象—— 类的6个默认成员函数及日期类的实现

C++初阶第五篇——类和对象(中)(构造函数+析构函数+拷贝构造函数+赋值操作符重载)

[C++11 类的改进] --- 继承控制:=default和=delete