为什么要使用构造函数而不是普通函数?

10

这是一个非常基础的问题,我已经搜索过了,但我想问问这个社区,我们既有 构造函数 又有 方法。但通常我们使用构造函数来初始化变量而不是使用方法。我认为两者都可以用于初始化变量。那么它们之间的基本区别是什么?是否有任何坚实的理由呢? 对于初学者来说,请容忍此基础问题。 提前致谢。

7个回答

8

最重要的区别:当您实例化一个对象时,它的构造函数将被调用,而调用方法总是可选的。因此,您可能会忘记调用初始化方法并未能正确地初始化所有内容。

例如,所有这些正常实例化对象的方式都会调用构造函数。

Foo* p = new Foo();
Foo p;

如果您有必填参数,请不要定义默认构造函数,而是要求使用参数进行构造:

class Foo
{
private:
   Foo();
public:
   Foo(int param1, double param2)
};

这种方式的好处在于你需要在实例化类之前先定义参数。因此你必须要这样做:

Foo* p = new Foo(1, 5.0);

如果使用无效的参数构建失败,则会出现编译器错误:

Foo* p = new Foo(); // compiler error

所以,尽可能地,总是在构造函数中进行初始化。有一些情况下,构造函数可能不可行。例如,唯一的失败构造函数的方法是使用异常。构造失败可能是“例行公事”,并不真正异常。此外,在某些体系结构上,异常可能是昂贵的。另一个情况可能是当您想要确保虚拟方法完全绑定时,这仅在构建后才能保证。


实际上我正在询问用户要求的一些初始化值,假设为了模拟目的,我们必须将大约20个变量初始化为给定值。如果每个变量都正确地初始化,那么在这种情况下,你会坚持你的观点吗? - Rafay Zia Mir
@kashmirilegion 请看我的修改。如果你需要初始化20个变量,你可以定义一个接受20个变量的构造函数或者一个接受结构体的构造函数。 - Doug T.

3

它们不能同时用于初始化成员变量。构造函数负责初始化成员,每当您创建新实例时会自动调用。

考虑以下示例:

class Foo {
public:
    // Constructor
    Foo() : x(53)   // Initialise x
    {}

    void bar() {
        x = 42;     // Error, attempt to *assign* a const member!
    }

private:
    const int x;
};

如果没有构造函数,就没有办法初始化成员变量 x


这很好,直到你需要在运行时返回错误代码。 :P - chrisaycock
+1 为你的好回答。谢谢你的论点给了我清晰构造函数的思路。 - Rafay Zia Mir

1

构造函数会自动调用,因此无需担心用户是否已调用初始化方法。然而,Google风格指南确实对构造函数有所规定

  • 除了使用异常之外,构造函数没有简单的方法来表示错误。
  • 如果工作失败,我们现在有一个初始化代码失败的对象,因此它可能处于不确定状态。
  • 如果工作调用虚函数,则这些调用将不会分派到子类实现。即使您的类当前没有被子类化,对类进行未来修改也可能悄悄地引入此问题,从而导致很多混乱。
  • 如果有人创建此类型的全局变量,则构造函数代码将在main()之前被调用,可能会破坏构造函数代码中的某些隐含假设。

Google的建议是在构造函数中进行简单的初始化,在单独的方法中进行复杂的初始化。


这很好,除非你是不可变对象的粉丝。 - Oliver Charlesworth
遵循Google的C++风格指南的主要论点似乎是大名鼎鼎。我完全不喜欢他们对(不)使用异常的看法。如果您不使用异常,则正在使用非常类似于C的C ++样式。一位著名的C ++专家曾经说过:http://www2.research.att.com/~bs/3rd_safe0.html回答您的项目列表:(1)那么只需使用异常(您刚刚发现它们对于使用构造函数/ RAII至关重要) (2)同样,如果抛出异常,则不会有无效对象 (3)我同意,可能会出现一些令人讨厌的问题,编译器应该警告您。 (4)所以,构造函数中没有代码? - Andre

0

构造函数可用于有效的内存管理,这是函数无法实现的。

析构函数可用于在不需要时销毁构造函数。

此外,使用复制构造函数已知可以防止由于内存错误而导致的困难或错误。


0

与函数不同,构造函数是在创建对象时自动调用的。此外,构造函数用于表明这是对象初始化的意图。


0

假设您有一个没有空构造函数的成员变量。那么您唯一的选择就是在构造函数的初始化列表中对其进行初始化。

此外,当您使用new运算符分配数组时,也会调用构造函数,而使用方法进行初始化将使代码更加复杂。

通常,在构造函数中具有复杂逻辑并不是一个很好的想法,但应该在其中进行简单的初始化(确保对象处于某种有效状态所需执行的操作)。


0

初始化变量无疑是一种非常重要的编程实践。在使用类时,选择将它们初始化在方法内部。因此,我们有两个步骤:

  1. 定义方法
  2. 调用该方法以执行初始化

但如果“调用方法”被遗忘,则变量最终会具有垃圾值。为了使程序员更轻松地工作,引入了构造函数方法的概念。

有了构造函数,我们只需要一步:

  1. 定义构造函数

当创建类的新对象时,“调用部分”会自动执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接