为什么不能在没有构造函数的情况下创建对象?

时间:2013-08-23 15:56:28

标签: c++ constructor

为什么创建对象需要构造函数? 即使我没有定义构造函数,也会生成一个默认构造函数。但是为什么需要一个构造函数呢?

5 个答案:

答案 0 :(得分:8)

  

为什么没有构造函数就不能创建对象?

这更多是关于术语的讨论,而不是关于行为的真实论证。正如你所提到的,在某些情况下,在构造过程中没有什么可做的,所以不需要构造函数,对吧?好吧,有一个琐碎构造函数的概念,它是一个根本不做任何事情的构造函数。为了标准文档,将所有内容视为具有(可能是微不足道的)构造函数比在必须在其当前仅表示“构造函数”的所有位置提供所有案例和异常更容易。

考虑到'构造函数'的每次使用都必须被'构造函数替换,如果类型没有任何虚函数或基础而没有任何成员需要生成构造函数,则无需替换。

这就是为什么所有虚拟函数都被称为 overrider 的原因,即使是层次结构中第一个虚拟函数覆盖也是如此。这种泛化形式使得语言稍微容易理解,尽管没有太多人会声称关于初始化的第8.5节很简单......

另请注意,虽然根据定义所有用户定义的类型都有构造函数,但构造函数不是 required 用于创建对象。特别是对于具有 trivial-constructor 的对象,生命周期在分配对象的内存时开始(标准,知道平凡意味着什么都不做甚至没有通过要求在这种情况下运行构造函数的跳跃。


3.8 [basic.life] / 1

  

对象的生命周期是对象的运行时属性。如果一个对象属于类或聚合类型,并且它或其成员之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非平凡的初始化。 [注意:通过简单的复制/移动构造函数进行初始化是非平凡的初始化。 - 结束注释]类型T对象的生命周期始于:

     

- 获得具有类型T的正确对齐和大小的存储,并且

     

- 如果对象具有非平凡的初始化,则其初始化完成。

用于读取的第二个项目符号(C ++ 03):如果T是具有非平凡构造函数(12.1)的类类型,则构造函数调用已完成。哪个更清楚地说明了不需要执行构造函数。但新措辞以基本相同的方式表达了意图。只有当对象具有非平凡初始化时,才需要完成初始化。对于具有 trivial-constructor 的对象(普通初始化),分配存储会创建对象。它在哪里重要?

struct T { int x; }; // implicitly defined trivial-constructor
T *p = static_cast<T*>(malloc(sizeof *p));

// *p is alive at this point, no need to do
T *q;
{ void *tmp = malloc(sizeof T);
  q = new (tmp) T;              // call constructor
}

答案 1 :(得分:1)

在某种程度上,这是一个略带哲学的问题。您可以将构造函数视为将一些未初始化的内存转换为对象的子例程。或者您可以将其视为一种语言功能,使初始化更容易遵循,编写和理解。你甚至可以循环地回答这个问题:为什么创建一个对象需要一个构造函数?因为在某种意义上,这就是对象的创建。如果您没有构造函数,那么您创建的内容不是对象。

可能是某个特定的构造函数什么都不做,但这是该类的实现细节。每个类都有一个构造函数这一事实意味着类封装需要进行哪些初始化:要安全地使用该类,您不需要知道构造函数是否执行任何操作。实际上,在存在继承,vtable,调试跟踪和其他编译器功能的情况下,您甚至可能不知道构造函数是否执行任何操作。 (C ++通过调用某些类POD类型使其稍微复杂化,但只要您不需要知道POD类型的某些内容,封装就会成立。)

构造函数的调用定义了创建对象的点。就语言语义而言,当构造函数完成时,构造对象存在:在此之前,对象不存在,并且使用它就像错误一样。这就是为什么构造顺序(即,调用成员对象和基类子对象构造函数的顺序)在C ++和类似语言中如此重要的原因:如果构造函数可以抛出异常,那么这是必要的准确地销毁那些已经构建的对象。

最终得到一个工作程序,程序员,任何试图理解源代码,编译器和链接器,运行时库以及任何其他编译工具的人都需要对程序的含义有一个共同的想法:程序的语义。同意对象的生命周期 - 当编译器可以运行额外的代码来帮助创建它时,以及何时可以安全地使用它 - 实际上是该协议的重要组成部分。构造函数和析构函数是定义此生命周期的一部分。即使它们有时是空的,它们也为我们提供了一种在对象有效时达成一致的方式,从而更容易(可能)指定理解语言

答案 2 :(得分:0)

假设您有一个简单的课程:

class Foo
{
    int bar;
}

bar的确切价值是多少?可能在你的对象分配内存时并不关心,但运行程序的机器需要给它一些价值。这就是构造函数的用途:将类成员初始化为某个值。

答案 3 :(得分:0)

构造函数是调用类成员的构造函数所必需的,除了内置类型see

答案 4 :(得分:0)

参数化构造函数可以接受参数。例如:

class example
{
     int p, q;
   public:
     example();
     example(int a, int b);                         //parameterized constructor
};
example :: example()
{
}
example :: example(int a, int b)
{
     p = a;
     q = b;
}

当在参数化构造函数中声明对象时,必须将初始值作为参数传递给构造函数。对象声明的正常方式可能不起作用。可以显式或隐式调用构造函数。隐式调用构造函数的方法也称为简写方法。

example e = example(0, 50);                     //explicit call

example e(0, 50);                               //implicit call

这对于为对象提供初始值特别有用。 此外,您还可以在此页面上找到重要内容: http://en.wikipedia.org/wiki/Constructor_%28object-oriented_programming%29