1. 概述
本篇继续C++面向对象编程,包括像类啊,构造函数啊所有这些东西。那么什么是构造函数呢,构造函数基本上是一种特殊类型的方法,他在每次实例化对象时运行。所以我认为用一个例子来描述是最好的。假设我们想要创建一个Entity类
2. 案例
1. 准备项目
准备一个简单的项目,项目中有一个main.cpp文件,内容如下
2. 开始项目
我们想创建一个Entity类,它有两个成员变量float x, y,也就是描述entity的位置。
如果我试着创建一个Entity实例,然后给它一个print函数,这样它就能把它的内容输出到控制台。
我们实例化Entity后调用print函数。
F5运行我们的代码
可以看到它正常工作。然而我们的得到的Entity的位置看是随机的值,这是因为当我们实例化Entity,并为它分配内存时,我们实际上并没有初始化那个内存,这意味着我们得到了那个内存空间里原来的那些东西。
我们可能想做的是初始化内存,并将它设为0之类的,这样我们的位置默认为0。如果我们不指定或者设置一个值的话。
后面会继续深入类初始化的文章,这里保持简短。另一个很好的例子,如果我们决定手动打印x和y,因为他们是public的,那么我们可以继续尝试写代码打印出x
我们尝试编译代码,ctrl + F7
可以看到,我们得到了一个错误,使用了未初始化的局部变量"e"
。也就是说,这个代码甚至都不会通过编译,因为我们试图使用没有初始化的内存。
这个Print()函数虽然可以通过编译,但是它显示的并不是我们所期望的,因为它打印的X和Y都设置了看似随机的值。
我们已经知道,需要某种初始化,我们需要一种方法,当构造一个Entity实例时,我们想要把X和Y设为0,除非已经被指定了其他值。我们可能想创建一个初始化的方法,因此我们来创建一个叫Init的方法。
现在,我们能做的是,当Entity对象实例创建时,我们可以调用e.Init()方法来初始化X,Y的值。
然后如果我试着打印我的值。F5运行程序
可以看到得到了0。当我们调用print()时,得到了0, 0。这意味着X和Y被设为0。
然而,我们额外编写了相当多的代码。我们需要定义Init方法,每当我们想在代码中创建一个Entity对象(实例),这意味着我们都需要实际运行Init函数。这就多了相当多的代码,相当的不清爽。
当我们构造对象时,如果我们有办法直接运行这个初始化代码就好了。于是,就有了构造函数
。
3. 构造函数
构造函数
是一种特殊类型的方法,这是一种每次你构造一个对象时都会调用的方法。我们像定义其他方法一样定义它。然而,它没有返回类型,并且它的名称必须与类的名称相同。
写Entity类的构造函数时,首先是输入类名Entity,后接小括号,我们可以选择给出一个参数,我们一会讲到,或者完全空白。Entity()
,然后我们可以给他一个函数主体,在这种情况下,我可以令X等于0,Y等于0
我们可以去掉Init的方法及其调用
如果现在运行代码 F5
会看到,我们得到的结果和Init方法得到的结果是一样的。然而,现在我们不需要Init方法了,Init方法被构造函数取代了。
4. 默认的构造函数
如果不指定构造函数,默认仍然会有一个构造函数,它只是一个叫做默认构造函数的东西。它默认情况下已经为我们准备好了。然而这个默认的构造函数实际上什么都没做。
它基本上和这个相等,函数体内完全空着。他什么也不做,没有为变量进行初始化。像java等语言,数据类型,比如int和float,会自动初始化为0。但C++不会,我们必须手动初始化所有基本类型。否则,它们将被设置为留在该内存中的其他什么值。所以非常非常重要的的是,不要忘记初始化。后续的文章会讲到更多关于初始化的内容,以及正确初始化内存的不同策略和方法。
现在,我们看一下带参数的构造函数,其实我们可以写很多的构造函数。当然,前提是它们有不同的参数。这个和我们写一些同名方法(函数)的时候是一样的。其实,这叫函数重载
,即有相同的函数(方法)名,但是有不同的参数的不同函数版本。
也就是,我们将参数赋值给了我们的成员变量。
现在,我们可以选择使用参数来构造Entity对象了。
如果我们运行程序,当然10和50打印到控制台。
到这里,这个构造函数讲的差不多了。构造函数,当然,如果不实例化对象,将不会运行。所以如果我们只使用一个类的静态方法,构造函数是不会运行的。我们还没有讨论堆内存的分配问题。后续的文章中肯定会讲到。当然,当使用new关键字并创建一个对象实例时,它也会调用构造函数。
5. 删除构造函数
这里也有一些方法可以删除构造函数,例如,我们有一个log类,它只有静态的日志方法。
我不希望有人创建log类的实例,有两种不同的解决方法。
1. private,构造函数私有化
我们可以通过设置为private来隐藏构造函数。
可以看到我们在Log l;
这里得到了一个错误,因为我们不能访问构造函数。
如果我们去掉private修饰构造
可以看到Log l;
显然是允许构造对象的。C++为我们提供了一个默认构造函数。
2. delete删除构造函数
然而,我们可以告诉编译器,我们不需要这个默认构造函数,我们可以简单的写Log() = delete;
我们不能像这样Log l;
这样调用log,因为默认构造函数已经被我们删除了,实际上并不存在。
还有一些特殊类型的构造函数,比如复制构造函数,还有移动构造函数,这每一个都是一篇单独的文章,因为它们都很复杂。但是对于基本的使用,这就是构造函数,一个特殊的方法,在我们创建类的实例时运行。它的主要用途是初始化该类,当我们创建一个新对象实例时,构造函数确保我们初始化了所有的内存,做了所有我们需要做的设置。