C++中是否有隐式默认构造函数?

Ski*_*ick 63 c++ constructor

在我正在阅读的书中(C++ Without Fear),它表示如果你没有为一个类声明一个默认构造函数,那么编译器会为你提供一个"零逻辑".我已经尝试过这个,而且我没有看到任何归零行为.我也找不到任何在Google上提到这一点的内容.这只是一个错误或特定编译器的怪癖?

Mar*_*ork 63

如果未定义构造函数,编译器将为您定义默认构造函数.

这个的实现

默认构造函数是:

  • default构造基类(如果基类没有默认构造函数,这是编译失败)
  • default按声明的顺序构造每个成员变量.(如果成员没有默认构造函数,则这是编译失败).

注意:
POD数据(int,float,pointer等)没有显式构造函数,但默认操作是什么都不做(在C++哲学的风向标中;除非我们明确要求,否则我们不想支付任何费用它).

如果没有定义析构函数/复制构造函数/赋值运算符,则编译器会为您构建其中一个(因此类总是有一个析构函数/复制构造函数/赋值运算符(除非您作弊并明确声明一个但不定义它)).
默认实现是:

析构函数:

  • 如果定义了用户定义的析构函数,请执行提供的代码.
  • 按声明的相反顺序调用每个成员的析构函数
  • 调用基类的析构函数.

复制构造函数:

  • 调用Base类Copy Constructor.
  • 按声明顺序为每个成员变量调用复制构造函数.

作业运营商:

  • 调用基类赋值运算符
  • 按声明顺序调用每个成员变量的赋值运算符.
  • 返回对此的引用.

注意POD数据的复制构造/分配操作符只是复制数据(因此与RAW指针相关的浅复制问题).

  • @wilhelmtell:那是__exlicitly__使用默认初始化的时候.在上述情况下,POD类型未显式默认初始化,因此值未定义.这就是为什么我使用默认构造函数稍微模糊的术语. (6认同)
  • 从标准中的第8.5节第5项开始:_"默认初始化T类型的对象意味着:...... [如果T是POD],对象是零初始化的."_和同一部分的第4项: _"对T类型的对象进行零初始化意味着:如果T是标量类型,则将对象设置为值0(零),作为整数常量表达式,转换为T"_.对于POD,如果我没有弄错,这意味着`int i = int();`以`i == 0结尾'结束 (4认同)

Bil*_*ard 38

我认为值得指出的是,如果你不提供任何构造函数,那么默认构造函数将只由编译器创建.这意味着如果您只提供一个带参数的构造函数,编译器将不会为您创建默认的no-arg构造函数.

您的书中讨论的归零行为可能特定于特定编译器.我一直认为它可以变化,你应该明确初始化任何数据成员.


nob*_*bar 36

  • 编译器是否自动生成默认构造函数?
  • 隐式生成的默认构造函数是否执行零初始化?

如果您在法律上解析2003标准的语言,那么答案是肯定的,不是.但是,这不是全部故事,因为与用户定义的默认构造函数不同,从头开始创建对象时并不总是使用隐式定义的默认构造函数 - 还有另外两种情况:无构造成员值初始化.

"无结构"的情况实际上只是一种技术性,因为它在功能上与调用普通的默认构造函数没有什么不同.另一种情况是更加有趣:成员明智值初始化通过使用调用"()" [仿佛明确调用一个没有参数的构造函数]和它绕过什么技术上称为默认构造函数.相反,它递归地对每个数据成员执行值初始化,对于原始数据类型,这最终解析为零初始化.

实际上,编译器提供了两个不同的隐式定义的默认构造函数.其中一个确实执行原始成员数据的零初始化,而另一个执行.以下是一些如何调用每种类型的构造函数的示例:

    MyClass a; // default-construction or no construction
    MyClass b = MyClass(); // member-wise value-initialization
Run Code Online (Sandbox Code Playgroud)

    new MyClass; // default-construction or no construction
    new MyClass(); // member-wise value-initialization
Run Code Online (Sandbox Code Playgroud)

注意:如果用户声明的默认构造函数确实存在,那么成员方式的值初始化只是调用它并停止.


这里有一个关于标准说明的详细细节......

  • 如果不声明构造函数,编译器会隐式创建默认构造函数[12.1-5]

  • 默认构造函数不初始化基元类型[12.1-7]

    MyClass() {} // implicitly defined constructor
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果使用"()"初始化对象,则不会直接调用默认构造函数.相反,它引发了一系列称为值初始化的规则[8.5-7]

  • 值初始化的净效果是永远不会调用隐式声明的默认构造函数.相反,调用一个递归的成员值初始化,它最终将零初始化任何原始成员,并在任何具有用户声明的构造函数的成员上调用默认构造函数[8.5-5]

  • 值初始化甚至适用于原始类型 - 它们将被零初始化.[8.5-5]

    a = int(); // equivalent to int a=0;
    
    Run Code Online (Sandbox Code Playgroud)

所有这些对于大多数目的来说都没有实际意义.类的编写者通常不能假设数据成员在隐式初始化序列期间将被清零 - 因此,如果任何自我管理类具有需要初始化的任何原始数据成员,则应该定义自己的构造函数.

那么这什么时候重要?

  • 在某些情况下,通用代码可能会强制初始化未知类型.值初始化提供了一种方法.请记住,如果用户提供了构造函数,则不会发生隐式零初始化.

  • 默认情况下,std :: vector包含的数据是值初始化的.这可以防止内存调试器识别与否则未初始化的内存缓冲区相关的逻辑错误.

    vector::resize( size_type sz, T c=T() ); // default c is "value-initialized"
    
    Run Code Online (Sandbox Code Playgroud)
  • 可以使用值初始化语法对整个基元类型或"普通旧数据"(POD)类型结构进行零初始化.

    new int[100]();
    
    Run Code Online (Sandbox Code Playgroud)

这篇文章提供了有关标准版本之间差异的更多细节,并且还指出了标准在主要编译器中的应用不同的情况.

  • 这是迄今为止最好的答案 - 不幸的是,我只能投票一次.所以其他人也应该投票! (4认同)

Tod*_*lin 20

C++确实会生成一个默认构造函数,但前提是你没有提供自己的构造函数.该标准没有说明将数据成员归零.默认情况下,当您第一次构造任何对象时,它们是未定义的.

这可能会令人困惑,因为大多数C++原语类型都有默认的"构造函数",它们将它们初始化为零(int(),bool(),double(),long()),但编译器不会调用他们像对象成员那样初始化POD成员.

值得注意的是,STL 确实使用这些构造函数来默认构造包含基本类型的容器的内容.您可以查看此问题,以获取有关STL容器中的内容如何进入的更多详细信息.


Ecl*_*pse 11

为类创建的默认构造函数不会初始化内置类型,但它将在所有用户定义的成员上调用默认构造函数:

class Foo
{
public:
     int x;
     Foo() : x(1) {}
};

class Bar
{
public:
     int y;
     Foo f;
     Foo *fp;
};

int main()
{

    Bar b1; 
    ASSERT(b1.f.x == 1); 
    // We know nothing about what b1.y is set to, or what b1.fp is set to.

    // The class members' initialization parallels normal stack initialization.
    int y;  
    Foo f; 
    Foo *fp; 
    ASSERT(f.x == 1);
    // We know nothing about what y is set to, or what fp is set to.

}
Run Code Online (Sandbox Code Playgroud)


And*_*ant 5

如果用户创建的构造函数和析构函数不存在,编译器将生成默认构造函数和析构函数.这些不会修改任何数据成员的状态.

在C++(和C)中,不保证任何已分配数据的内容.在调试配置中,某些平台会将此值设置为已知值(例如0xFEFEFEFE)以帮助识别错误,但不应依赖此错误.


cod*_*gic 5

清零仅发生在全局变量中。因此,如果您的对象是在全局范围内声明的,则其成员将被清零:

class Blah
{
public:
    int x;
    int y;
};

Blah global;

int main(int argc, char **argv) {
    Blah local;
    cout<<global.x<<endl;  // will be 0
    cout<<local.x<<endl;   // will be random
}
Run Code Online (Sandbox Code Playgroud)