-1

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

4

5 回答 5

8

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

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

考虑到每次使用 'constructor' 都必须替换为 'constructor or nothing 如果该类型没有任何虚函数或基并且没有需要生成构造函数的成员'。

这就是为什么所有虚函数都被称为覆盖器的原因,即使是层次结构中的第一个,根据定义不会覆盖任何东西。这种形式的概括使语言更容易解释,尽管没有太多人会声称第 8.5 节关于初始化很简单......

另请注意,虽然所有用户定义的类型都具有构造函数,但创建对象不需要构造函数。特别是对于具有琐碎构造函数的对象,生命周期从分配对象的内存时开始(标准,知道琐碎意味着什么都不做,甚至不需要在这种情况下运行构造函数。


3.8 [basic.life]/1

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

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

-- 如果对象有非平凡的初始化,它的初始化就完成了。

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

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
}
于 2013-08-23T16:04:54.933 回答
1

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

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

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

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

于 2013-08-23T16:25:48.867 回答
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

于 2013-08-23T16:25:56.163 回答
0

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

于 2013-08-23T16:05:19.357 回答
0

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

class Foo
{
    int bar;
}

究竟价值bar是多少?也许不在乎你的对象什么时候被分配了它的内存,但是运行你的程序的机器需要给它一些价值。这就是构造函数的用途:将类成员初始化为某个值。

于 2013-08-23T16:00:32.293 回答