我们继续学习 C++ 的面向对象编程,本期主要是讲其中的 构造函数。
什么是构造函数呢?
构造函数基本上是一种特殊类型的方法,它在每次实例化对象时运行。
我们直接来看一个例子吧。
例子时间
我们将要通过创建一个 Entity 类来深入了解这个知识点。
这个类有两个成员变量,float X、Y,用来描述 Entity 的位置。然后创建一个 Entity 实例。再给它一个 Print 函数,这样它就能够将它的位置信息输出到控制台。
运行这段代码,结果是完全正常的,没有任何问题。
然而你应该会注意到,输出的值看起来像是随机的值。
这是因为当我们实例化 Entity 为它分配内存时,我们实际上并没有初始化那个内存空间,这意味着我们实际输出了那个内存空间里面原来的值。
我们还可以从另一个角度说明一下当前遇到的情况。
因为 X 和 Y 是公有的,所以我们可以在主函数中手动打印 X。
调试代码,我们会得到一个错误,未初始化局部变量。换句话说,我们试图使用没有初始化的内存,这个编译器不允许这样操作。
这个 Print 函数虽然可以通过编译,是它显示的并不是我们所期望的。因为它直接打印 X 和 Y,但是它们都设置了看似随机的值,这个不太一样。
看起来接下来的任务就是初始化内存了,我们需要将它设置为 0。——我们期望在不指定一个值的情况下,这个位置的值默认为 0。
我需要一种方法,当构造一个 Entity 的实例时,我们可以把 X 和 Y 设为 0,除非它们已经被指定了其它的值。
解决问题的方法之一是直接创建一个初始化的方法。
我们可以创建 Init 方法。
它是 void 类型的函数,你可以看到它只是用来设置 X 和 Y 的值为 0的。
现在我们能做的就是当 Entity 对象实例创建时,可以调用这个 Init 方法。
调试运行程序,可以看到 X 和 Y 被设为了0 ,看起来我们完成了我们的要求。
然而,如果这样操作的话,我们会额外编写相当多的代码。每当你想在代码中创建一个对象,我们都需要调用 Init 函数,这样当代码越来越多的时候,写起来也会越来越麻烦。
当构造对象时,如果我们有办法直接运行这个初始化代码就好了。于是,就有了构造函数。
构造函数
构造函数是一种特殊类型的方法,这是一种每次你构造一个对象时都会自动调用的方法。
我们像定义其它方法一样定义它。然而特殊的是,它没有返回类型,并且它的名字必须与类的名称相同。
写 Entity 的构造函数时,首先输入类名 Entity 。
可以选择给出一个参数,这个我们一会儿会讲到,参数的位置先空着。
然后可以给它一个函数主体。
在这种情况下,和之前一样让 X等于 0,Y 也等于 0。删掉 Init 方法,现在已经不再需要它了。
运行一下代码,你会发现和前面用 Init 方法是一样的效果。—— Init 方法被构造函数取代了。
实际情况是,如果你不指定构造函数,其实仍旧有一个构造函数。只不过它是一个叫做默认构造函数的东西,默认情况下已经为你准备好了,只不过默认构造函数实际上什么都没做,这和我们定义的这个构造函数函数体内完全空着是一样的效果。
像 Java 类的语言,基本数据类型比如 int 和 float,会自动初始化为0。但 C++ 的情况并非如此,你必须手动初始化所有基本类型,否则它们将被设置为原本该内存中值。
所以初始化是非常重要的。
以后的系列中,我会讲到更多关于初始化的内容以及正确初始化内存的不同策略和方法。
接下来让我们看一下带参数的构造函数。
没错,其实你可以写很多的构造函数,前提是它们有不同的参数。这其实和我之前写一些同名方法的操作是一样的。这个过程叫函数重载。可以这样说,它们是 有相同的函数名,但是有不同的参数的不同函数版本。
这里我加上 x 和 y 作为参数,在函数体中然后把 x 和 y 的值赋值给 X 和 Y。
我现在可以选择使用参数来构造 Entity 对象了。
运行程序之后,可以看到 10 和 5 出现在控制台,很棒哈。
构造函数讲的差不多了。
还有一些我需要提醒大家。
如果不实例化对象,构造函数将不会运行,所以如果你只使用一个类的静态方法,它是不会运行。我们还没有讨论堆内存的分配问题,在之后的是系列中会讲到的。
当使用 new 关键字并创建一个对象实例时,也会调用构造函数。
删除构造函数
也有一些方法可以删除构造函数。
举个例子。
我们有一个 Log 类,它只有静态的日志方法,我只是想让人们像这样使用 Log 类,不创造实例而调用 Write 方法。
有两种不同的解决方法。
我们可以通过设置为私有(private)来隐藏构造函数。
你可以看到 40 行得到一个错误,因为我不能访问构造函数,而之前不这样做,显然是允许构造对象的。
C++ 为我们提供了一个默认构造函数。然而我可以告诉编译器:我不想要那个构造函数了。
我们可以这样操作。
可以看到实例化的地方有错误,因为默认构造函数并不存在,它已经被删掉了。
最后的话
还有一些特殊类型的构造函数,如复制构造函数、移动构造函数等。它们每一个都会单独有一期,因为它们都很复杂。
这就是构造函数的基本使用了,记住一句话就行:它是一个特殊的方法,在你创建类的实例时运行,主要用途是初始化该类。当你创建一个新对象实例时,构造函数确保你初始化的所有内存,完成你所有需要做的设置。
本期就是这些。如果你有什么想法,可以放在评论区哈,下期再见。