构造函数
在说这个之前我想问一个问题,如果成员变量是私有的,我们怎么初始化这些私有的成员变量?
其实这就是构造函数的存在的理由,因为这个成员变量为私有的,要对它们进行初始化,必须用一个
公有成员函数来进行。同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的函数称为构
造函数(constructor)
下面就是构造函数的特征:
1.函数名与类名相同。
2.无返回值。
3.对象构造(对象实例化)时系统自动调用对应的构造函数。
4.构造函数可以重载。
5.构造函数可以在类中定义,也可以在类外定义。
6.如果类定义中没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个
构造函数,系统就不会自动生成缺省的构造函数。
7.无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个。
下面我们举个例子:
定义一个Date类,在它尝试使用一下构造函数。
class Date
{
public:
// 1.无参构造函数
Date()
{}
// 2.带参构造函数
Date(int year, int month, int day)
{
cout << "我定义的构造函数成功运行了" << endl;
_year = year;
_month = month;
_day = day;
}
void Display()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void TestDate1()
{
Date d1; // 调用无参构造函数
d1.Display();
}
int main()
{
TestDate1();
system("pause");
return 0;
}
现在我们试试有参的构造函数:
class Date
{
public:
// 1.无参构造函数
Date()
{}
// 2.带参构造函数
Date(int year, int month, int day)
{
cout << "我定义的构造函数成功运行了" << endl;
_year = year;
_month = month;
_day = day;
}
void Display()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void TestDate1()
{
Date d2(2015, 1, 1); // 调用带参的构造函数
d2.Display();
}
int main()
{
TestDate1();
system("pause");
return 0;
}
因为我们知构造函数是可以重载的,所以我们这次使用有参的构造函数。结果应该是我首先输出一段话
,然后依次打印出成员变量,我们来看看:
还有一种缺省参数的构造函数,我们分别来看看他们的结果:
class Date
{
public:
// 3.缺省参数的构造函数
Date(int year = 2000, int month = 1, int day = 1)
{
cout << "我调用成功了" << endl;
_year = year;
_month = month;
_day = day;
}
// 4.半缺省参数的构造函数(不常用)
Date(int year, int month = 1)
{
_year = year;
_month = month;
_day = 1;
}
void Display()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1; // 调用缺省构造函数
d1.Display();
}
int main()
{
Test();
system("pause");
return 0;
}
看看结果,因为它早早地已经有自己的值了,所以就算没有参数也没有关系
class Date
{
public:
// 3.缺省参数的构造函数
Date(int year = 2000, int month = 1, int day = 1)
{
cout << "我调用成功了" << endl;
_year = year;
_month = month;
_day = day;
}
// 4.半缺省参数的构造函数(不常用)
Date(int year, int month = 1)
{
_year = year;
_month = month;
_day = 1;
}
void Display()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//这里注意半缺席的构造函数,参数从前往后可以缺省,但是不能从最后一位开始缺省
void Test()
{
Date d2(2015,3,2); // 调用缺省构造函数
d2.Display();
}
int main()
{
Test();
system("pause");
return 0;
}
备注:若缺省参数声明和定义分离,则可以在声明或定义中给默认参数。
拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数(Copy Constructor)
,拷贝构造函数是特殊的构造函数。
特征:
1.拷贝构造函数其实是一个构造函数的重载。
2.拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
3.若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化
举个Date类,练习一下拷贝构造。
class Date
{
public:
Date(int year, int month , int day)
{
cout << "构造调用成功了" << endl;
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
Date(const Date& d)
{
cout << "拷贝构造被调用了" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
void Display()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void TestDate1()
{
Date d1(2017,3,6);
// 下面两种用法都是调用拷贝构造函数,是等价的。
Date d2(d1); // 调用拷贝构造函数
Date d3 = d1; // 调用拷贝构造函数
d2.Display();
d3.Display();
}
int main()
{
TestDate1();
system("pause");
return 0;
}
这里我们可以看到调用一次构造函数,调用两次拷贝构造,然后让我们看看结果吧。
拷贝构造函数特征第二条,拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
这个为什么不行呢?
思考一下,如果我们使用值传递,传参的时候是不是需要创建一个新的形参,而这个新的形参就值本身,
也就是自身的一个拷贝,然后呢,又调用了拷贝构造,然后就无穷递归了。
还有一个问题。