【问题标题】:Why do constructors not return values?为什么构造函数不返回值?
【发布时间】:2009-11-24 06:54:50
【问题描述】:

请告诉我为什么构造函数没有返回任何值。我想要一个完美的技术理由来向我的学生解释为什么构造函数没有任何返回类型。

【问题讨论】:

  • 这是什么语言的问题?
  • 因为它返回类的实例。如果它返回数据类型,你将如何创建一个新实例。
  • @Karan 没错。构造函数返回一个类的实例,因此它们不能有任何其他返回值。

标签: constructor


【解决方案1】:

构造函数实际发生的情况是,运行时使用编译器生成的类型数据来确定在内存中存储对象实例需要多少空间,无论是在堆栈上还是在堆上。

这个空间包括所有成员变量和 vtbl。分配此空间后,将调用构造函数作为实例化和初始化过程的内部部分来初始化字段的内容。

然后,当构造函数退出时,运行时返回新创建的实例。所以构造函数没有返回值的原因是因为它不是由你的代码直接调用的,它是由运行时的内存分配和对象初始化代码调用的。

它的返回值(如果它在编译成机器代码时实际上有一个)对用户是不透明的 - 因此,你不能指定它。

【讨论】:

  • 我们可以假设您描述的是 .NET 吗?
  • 尽管这在技术上是详细且正确的,但恕我直言,一个简单的“它不是为此而设计的”就足够了。当然,将运行时设计为返回构造函数的返回值而不是新创建的实例并不是不可能的。它只是没有多大意义,因此不是为了做到这一点而设计的。 :)
  • 我对 .Net 不太确定——以上是我对 C++ 采用的方法的理解。我确信 .Net 非常相似,除了编译器生成的元数据包含的不仅仅是实例化它所需的空间量(并且 c++ 也存储了一些关于类型的元数据,但在 .Net 支持的规模上没有任何内容。网)。
  • 至少在 C++ 中,构造函数不可能返回新创建的对象。它必须进行复制,这意味着调用复制构造函数,it 会返回什么?但是,您可以返回参考。不过,到底回到哪里?
  • 我们可以对__del__说同样的话吗?
【解决方案2】:

嗯,在某种程度上它返回了刚刚构造的实例。

你甚至可以这样称呼它,例如是Java

 Object o = new Something();

看起来就像调用带有返回值的“常规”方法

 Object o = someMethod();

【讨论】:

  • 那么为什么不允许该方法返回null?我不相信。一点也不。
  • 如果您知道唯一可能的最终状态是“您有一个 Something 实例”或“抛出异常”,那么使用构造函数会变得更加容易。 (我们用 C 尝试了整个“让调用者检查每个返回值”的事情。)拥有两个正交的错误报告机制是否有好处,调用者每次都必须检查,或者你是否建议也取消异常?
【解决方案3】:

(我偏向于 C++,所以对于其他语言,请持保留态度。)

简短回答:您不希望在代码中明确检查每个对象构造是否成功。

稍微长一点的答案:在 C++ 中,构造函数被动态调用,也被全局自动分配的对象调用。在这段代码中

void f()
{
  std::string s;
}

s (std::string::string()) 的构造函数无法返回任何值。要么成功 - 然后我们可以使用该对象,要么它引发异常 - 我们永远没有机会尝试使用它。

IMO,这就是它应该的方式。

【讨论】:

    【解决方案4】:

    构造函数应该如何返回返回值? new 运算符返回新创建的实例。你不叫演员,new做。

    MyClass instance = new MyClass();
    

    如果 ctor 会返回一个值,如下所示:

    public int MyClass()
    {
        return 42;
    }
    

    你会在哪里收到整数?

    【讨论】:

    • "构造函数应该如何返回返回值?"它可以返回this。返回null表示构造失败。
    • 但是当构造函数开始运行时,'this' 必须已经存在。如果您的构造“失败”,则“this”已经分配,​​然后会发生什么?你有一个部分构造的对象吗?是否应该调用析构函数?在 C++ 中,如果你抛出构造函数,你应该在僵尸对象中保存足够的状态,以便它可以自行清理;没有对象,它是怎么做到的?或者你现在有 2 个完全独立的构造函数错误机制?
    • @sbi 由于您不直接调用 ctro,因此它无法返回任何内容。指向新实例的引用/指针由 new 返回,而不是 ctor。
    【解决方案5】:

    构造函数是在初始化对象的新实例时自动调用的一些方法。

    如果您需要将对象初始化为给定状态并运行一些默认方法,则可以使用此方法。

    实际上你可以想象构造函数总是返回创建的对象的实例,这将是一个好的图像。

    【讨论】:

    • 好吧,鉴于您仍然需要声明 which 构造函数必须运行(通过包含正确的参数)我认为这并不真正符合“自动”的条件:- )
    • 好吧,考虑到大多数 OOPL 的语法,我会说,因为您仍然编写类型名称、参数以及在某些语言(Delphi、Eiffel)中编写构造函数的名称,因此编译器调用它类似于说当你写出方法调用时,编译器会调用一个方法。
    【解决方案6】:

    当您调用构造函数时,返回值是新对象:

    Point pt = new Point(1,2);
    

    但是在构造函数本身中,您实际上并没有创建和返回对象;它是在您的代码开始之前创建的,您只是在设置初始值。

    Point::Point(int x, int y) {
      this->x = x;
      this->y = y;
    }
    

    没有返回类型反映了构造函数的使用与其他函数不同的事实。 null 的返回类型虽然在技术上是准确的,但并不能很好地反映代码被使用 的事实,就好像它返回一个对象一样。但是,任何其他返回类型都表明您的代码应该以 return 结尾,这也是不正确的。

    【讨论】:

      【解决方案7】:

      所有答案都偏向于 C++/Java。构造函数没有理由不返回语言设计以外的值。

      从更广泛的意义上看构造函数:它是一个构造新对象的函数。您可以在 C 中编写完全有效的构造函数:

      typedef struct object object;
      int object_create( object **this );
      

      这是 C 语言中完美的 OOP,构造函数返回值(这也可以称为工厂,但名称取决于意图)。

      然而,为了自动创建对象(例如满足某种类型转换或转换),必须定义一些规则。在 C++ 中,有一个无参数的构造函数,如果没有定义,编译器会推断它。


      讨论比我们想象的要广泛。面向对象编程是一个描述编程的思维方式的名称。您几乎可以使用任何语言使用 OO:您所需要的只是结构和函数。 C++ 和 Java 等主流语言非常普遍,以至于我们认为它们定义了“方式”。现在看看 Ada 中的 OO 模型:它与 C++ 的模型相去甚远,但仍然是 OO。我相信像 Lisp 这样的语言还有其他一些做 OO 的方法。

      【讨论】:

      • 在这种情况下继承如何工作?如果用户提供的构造函数代码接受对可能为空的BaseType 对象的引用并对其进行适当设置,则如果给定DerivedType 对象,则此类代码将没有问题。但是,如果代码返回了 BaseType 对象,那么如何使用它来生成 DerivedType 对象就不太清楚了。
      【解决方案8】:

      尚未讨论的一点是,类“foo”的构造函数必须不仅在创建 foo 的实例时可用,而且在创建从 foo 派生的类的实例时也必须可用。在没有泛型(在设计 Java、C++ 或 .net 时不可用)的情况下,foo 的构造函数将无法返回任何派生类的对象。因此,需要通过其他方式创建派生类对象,然后将其提供给 foo 的构造函数(然后在进行初始化时,它将能够将相关对象用作 foo)。

      【讨论】:

        【解决方案9】:

        尽管构造函数的 VM 实现不返回任何值,但实际上它确实会返回任何值 - 新对象的引用。如果能够在一个语句中存储新对象的引用和附加返回值中的一个或两个,这在语法上会很奇怪和/或令人困惑。

        【讨论】:

          【解决方案10】:

          所以构造函数没有返回值的原因是因为它不是由您的代码直接调用的,而是由运行时中的内存分配和对象初始化代码调用的。它的返回值(如果它在编译为机器代码时实际上有一个)对用户是不透明的 - 因此,您不能指定它。

          【讨论】:

            【解决方案11】:

            构造函数不是由用户代码直接调用的。它由运行时的内存分配和对象初始化代码调用。它的值对用户是不可见的。

            【讨论】:

              【解决方案12】:

              构造函数不返回任何东西,甚至是 Void。尽管一些答案提到 Constructor 确实返回对新创建的 object 的引用,但事实并非如此。返回对象的是 new 运算符。

              那么为什么构造函数不返回任何值

              因为它不应该返回任何东西。构造函数的全部目的是通过设置初始值来初始化对象的当前状态。

              那么为什么它甚至不返回 Void

              这实际上是一个设计约束,已将其与方法区分开来。 public void className() 在 java 中是完全合法的,但它表示方法而不是构造函数。为了让编译器明白它是一个构造函数,它需要一种区分它的方法。

              【讨论】:

                【解决方案13】:

                在 C# 中,声明对象的语法是:

                classname objectname= new constructor();
                

                根据这一行,如果我们使用赋值运算符(=),那么它应该返回一些值。但是构造函数的主要目标是为变量赋值,所以当我们使用 new 关键字时,它会创建该类的实例,并且构造函数会为该特定对象实例的变量赋值,因此构造函数会返回该对象的赋值实例。

                【讨论】:

                  【解决方案14】:

                  我们不能独立调用构造函数。而是在创建对象时自动调用它们。 例如:

                  MyDate md = new Mydate(22,12,2012);
                  

                  在上面的示例中,new 将返回一个内存位置,该位置将由 md 保存,并且在编程上我们不能在单个语句中返回多个值。 所以构造函数不能返回任何东西。

                  【讨论】:

                    【解决方案15】:

                    根据我对 OO 设计方法的了解,我想说的是:

                    1)通过允许构造函数返回一个值,框架开发人员将允许程序在返回值未被处理的瞬间崩溃。为了保持程序工作流程的完整性,不允许从对象的初始化返回值是一个有效的决定。相反,语言设计者会建议/强制编码人员使用 getter/setter - 访问方法。

                    2) 允许对象在初始化时返回值也会导致可能的信息泄漏。特别是当对变量/方法应用了多个层或访问修改时。

                    【讨论】:

                      【解决方案16】:

                      正如你所知道的,当创建对象时,构造函数将被自动调用所以现在想象构造函数返回一个 int 值。所以代码应该是这样的......

                      Class ABC
                      {
                           int i;
                      public:
                           int ABC()
                           {
                              i=0;
                              return i;
                           }
                           .......
                      };
                      int main()
                      {
                          int k= ABC abc; //constructor is called so we have to store the value return by it
                          ....
                      }
                      

                      但正如您所知,int k= ABC abc; 这样的语句在任何编程语言中都是不可能的。希望你能理解。

                      【讨论】:

                        【解决方案17】:

                        i found it helpful

                        这种混淆源于构造函数与类定义的任何其他函数/方法一样的假设。不,他们不是。

                        构造函数只是对象创建过程的一部分。它们不像其他成员函数那样被调用。

                        【讨论】:

                          【解决方案18】:

                          我会在答案中使用 Java 作为我的语言。

                          class SayHelloOnCreation {
                               public SayHelloOnCreation() {
                                   System.out.println("Hello, Thanks For Creating me!");
                               }
                          }
                          
                          class Test {
                               public static void main(String[]args) { 
                                   SayHelloOnCreation thing = new SayHelloOnCreation(); //This line here, produces an output - Hello, Thanks For Creating me!
                               }
                          }
                          

                          现在让我们看看这里发生了什么。在 java 中,我们使用 new 关键字来创建一个类的实例。正如您在代码中看到的,在 SayHelloOnCreation thing = new SayHelloOnCreation(); 行中,赋值运算符之后的表达式在赋值完成之前运行。因此,使用关键字 new,我们调用该类的构造函数 (SayHelloOnCreation()),该构造函数在 Java 堆上创建一个 object。创建对象后,对该对象的引用将分配给 SayHelloOnCreation 类型的 thing 引用。

                          我要在这里强调的一点是,如果允许构造函数具有返回类型,首先语言的强类型特性将受到损害(请记住,我在这里谈论的是 Java)。

                          其次,这里创建了一个SayHelloOnCreation类的对象,所以默认情况下我猜构造函数返回相同类型的引用,以避免ClassCastException强>。

                          【讨论】:

                            【解决方案19】:

                            当显式调用时,方法会将值返回给调用者方法。由于没有显式调用构造函数,因此它将值返回给谁。构造函数的唯一目的是初始化类的成员变量。

                            【讨论】:

                              猜你喜欢
                              • 2011-03-21
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2012-08-07
                              • 2010-12-30
                              相关资源
                              最近更新 更多