构造函数是一种随着对象创建而自动被调用的函数,它的主要用途是为对象作初始化。那么,构造函数到底是什么样子的呢?
在C++中,规定与类同名的成员函数就是构造函数。需要注意的是,构造函数应该是一个公有的成员函数,并且构造函数没有返回值类型。以下是我们为链表结点类编写的一个构造函数:(其他成员函数定义见14.3节)
//node.h
- #include <iostream>//如果不包含iostream头文件,这个文件里就不能用cout
- using namespace std;
- class Node//定义一个链表结点类
- {
- public:
- ……
- Node();//构造函数的声明,构造函数是公有的成员函数,没有返回值类型
- ……
- private:
- int idata;//存储数据保密
- char cdata;//存储数据保密
- Node *prior;//前驱结点的存储位置保密
- Node *next;//后继结点的存储位置保密
- };
-
- Node::Node()//构造函数的定义
- {
- cout <<"Node constructor is running..." <<endl;//提示构造函数运行
- idata=0;//初始化idata
- cdata='0';//初始化cdata
- prior=NULL;//初始化前驱结点指针
- next=NULL;//初始化后续结点指针
- }
这时,我们创建一个链表结点对象,构造函数随着对象创建而自动被调用,所以这个对象创建之后idata的值为0,cdata的值为'0',prior和next的值都是NULL:(程序15.2.1)
//main.cpp
- #include <iostream>
- #include "node.h"
- using namespace std;
- int main()
- {
- Node a;//创建一个链表结点对象a,调用构造函数
- cout <<a.readi() <<endl;
- cout <<a.readc() <<endl;
- return 0;
- }
-
运行结果:
Node constructor is running...
0
0
可是,这样的构造函数还是不太理想。如果每次初始化的值都是固定的,那么有没有构造函数都是一样的。构造函数变成了一种摆设!我们该怎么办?
函数的特征之一就是能够在调用时带上参数。既然构造函数也是函数,那么我们就能给构造函数带上参数,使用重载或默认参数等方法,从而实现更自由地对对象进行初始化操作。以下便是对链表结点类的进一步修改:(程序15.2.2)
//node.h
- #include <iostream>
- using namespace std;
- class Node//定义一个链表结点类
- {
- public:
- Node();//构造函数0
- Node(int i,char c='0');//构造函数重载1,参数c默认为'0'
- Node(int i,char c,Node *p,Node *n);//构造函数重载2
- int readi() const;//读取idata
- char readc() const;//读取cdata
- Node * readp() const;//读取上一个结点的位置
- Node * readn() const;//读取下一个结点的位置
- bool set(int i);//重载,通过该函数修改idata
- bool set(char c);//重载,通过该函数修改cdata
- bool setp(Node *p);//通过该函数设置前驱结点
- bool setn(Node *n);//通过该函数设置后继结点
- private:
- int idata;//存储数据保密
- char cdata;//存储数据保密
- Node *prior;//前驱结点的存储位置保密
- Node *next;//后继结点的存储位置保密
- };
- int Node::readi() const//成员函数readi的定义
- {
- return idata;
- }
-
- char Node::readc() const
- {
- return cdata;
- }
- Node * Node::readp() const
- {
- return prior;
- }
- Node * Node::readn() const
- {
- return next;
- }
- bool Node::set(int i)//重载成员函数定义
- {
- idata=i;
- return true;
- }
- bool Node::set(char c)
- {
- cdata=c;
- return true;
- }
- bool Node::setp(Node *p)
- {
- prior=p;
- return true;
- }
- bool Node::setn(Node *n)
- {
- next=n;
- return true;
- }
- Node::Node()//构造函数0的定义
- {
- cout <<"Node constructor is running..." <<endl;//提示构造函数运行
- idata=0;//初始化idata
- cdata='0';//初始化cdata
- prior=NULL;//初始化前驱结点指针
- next=NULL;//初始化后续结点指针
- }
- Node::Node(int i,char c)//构造函数重载1,默认参数只需要在函数原型中出现
- {
- cout <<"Node constructor is running..." <<endl;
- idata=i;
- cdata=c;
- prior=NULL;
- next=NULL;
- }
- Node::Node(int i,char c,Node *p,Node *n)//构造函数重载2
- {
- cout <<"Node constructor is running..." <<endl;
- idata=i;
- cdata=c;
- prior=p;
- next=n;
- }
//main.cpp
- #include <iostream>
- #include "node.h"
- using namespace std;
- int main()
- {
- Node a;//创建一个链表结点对象a,调用构造函数0
- Node b(8);//创建一个链表结点对象b,调用构造函数重载1,参数c默认为'0'
- Node c(8,'F',NULL,NULL);//创建一个链表结点对象c,调用构造函数重载2
- cout <<a.readi() <<' ' <<a.readc() <<endl;
- cout <<b.readi() <<' ' <<b.readc() <<endl;
- cout <<c.readi() <<' ' <<c.readc() <<endl;
- return 0;
- }
运行结果:
Node constructor is running...
Node constructor is running...
Node constructor is running...
0 0
8 0
8 F
我们看到,在参数和重载的帮助下,我们可以设计出适合各种场合的构造函数。初始化各个对象的成员数据对我们来说已经是小菜一碟了。但是,这时你是否会回想起当初没有编写构造函数时的情形?如果没有编写构造函数,对象的创建是一个怎样的过程呢?
在C++中,每个类都有且必须有构造函数。如果用户没有自行编写构造函数,则C++自动提供一个无参数的构造函数,称为默认构造函数。这个默认构造函数不做任何初始化工作。一旦用户编写了构造函数,则这个无参数的默认构造函数就消失了。如果用户还希望能有一个无参数的构造函数,必须自行编写。