学过之前的类和对象前篇之后,下来就学到了类的6个默认成员函数了,
《构造函数》,《析构函数》,《拷贝构造函数》,《赋值操作符的重载》,
《const成员函数》,《取地址及const取地址操作重载》,下来就一个一个看,
----》《构造函数》《-----
**构造函数:**是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
特性
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主
要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
下来就看一看构造函数的代码:
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1998,int month = 1,int day = 1)
{
_year = year;
_month = month;
_day = day;
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
void SetDate(int year = 2019, int month = 1 ,int day = 14)
{
_year = year;
_month = month;
_day = day;
}
//2 计算机自己给出;
/*Date()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}*/ //没有函数调用的时候就直接由计算机给出随机的地址值。
//3 用户自己定义;
/* Date()
{
_year = 2019;
_month = 1;
_day = 15;
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}*/
//4// 无参构造函数
Date ()
{}
void Display()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
void Display()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1; //当是第一种的时候,计算机就会自己调运之前预先写好的值,
d1.SetDate();
d1.Display();
Date d2;
d2.SetDate(2019,1,16); //当自己定义的时候,计算机首先会使用用户写值
d2.Display();
return 0;
}
先给出前面两段给出的运行程序;
当你运行前面两段代码的时候,就可深刻体会到了,计算的缺省参数,半缺省,全缺省,当自己不写和自己写了之间的差别;后面的2和 3,我就没有运行,可以自己去运行一下。前面的说的自己调运或者计算机调用程序的过程,这个过程其实很简单,没有什么复杂的过程。
----》《析构函数》《-----
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
----> 特性 <----
析构函数是特殊的成员函数。
其特征如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值。
- 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
下面就看一下代码:下面就看一下代码:
//前面的和上面《析构函数的一样》
//这个"~"就是析构函数代表一样:
~Date()
{
cout <<"~Date()"<<endl;
}
int main()
{
Date d1;
Date d2(2019,1,15);
Date d3;
}
可以看出来:当我调运了三次函数,每次函数结束,系统会自动调用析构函数,所以析构函数也就调运了三次。
----》《拷贝构造函数》《----
<构造函数>:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
----<特征>----
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
下来我们就看一看代码:
Date(int year = 1998,int month = 1,int day = 1)
{
_year = year;
_month = month;
_day = day;
cout <<_year<< "-" <<_month << "-"<< _day <<endl; //打印d1;
}
Date(const Date& d) //拷贝构造函数的
{
_year = d._year;
_month = d._month;
_day = d._day;
cout <<_year<< "-" <<_month << "-"<< _day <<endl; //打印d2;
}
int main()
{
Date d1;
Date d2(d1); //调用拷贝构造的方法
return 0;
}
下来我们就可以看一下运行的结果:
由结果可以看出,d2将第一的值,拷贝了过去。
下来我们就看一下接下来的;
——《赋值运算符重载》——
一 《运算符重载》
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型或者枚举类型的操作数
3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
5.操作符有一个默认的形参this,限定为第一个形参 .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
下来我们看一下代码:
Date(int year = 1998 ,int month = 1 ,int day = 1)
{
_year = year;
_month = month;
_day = day;
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
bool operator==(const Date&d2)//在operator里面只能有一个定义;
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
int main()
{
Date d1;
//Date d(2019,1,15);
Date d2(2019,1,16);
cout<<(d1 == d2)<<endl; //这里也可以写成d1 == d2;
return 0;
}
看一下运行的程序;
这里可以看出,当我们比较两个日期是否相同,就可以用运算符的重载。
二 《赋值运算符重载》
直接看下面一段代码:
Date(int year = 1998 ,int month = 1 ,int day = 1)
{
_year = year;
_month = month;
_day = day;
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
/* bool operator==(const Date&d2)//在operator里面只能有一个定义; //这里定义为第一段代码:
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}*/
Date& operator=(const Date& d) //这里定义为第二段代码
{
if(this != &d)
{
_year == d._year
&& _month == d._month
&& _day == d._day;
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
else
return *this;
}
int main()
{
Date d1;
Date d2(2019,1,21);
// 这里d1调用的编译器生成operator=完成拷贝,d2和d1的值也是一样的。
d1 = d2;
}
我们开看一下结果:
就这样,我们可以把一个日期拷贝给另外一个,直接考虑用的是赋值运算重载,提高效率。
赋值运算符主要有四点:
- 参数类型
- 返回值
- 检测是否自己给自己赋值
- 返回*this
- 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
——《const成员函数》——
将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。 《const修饰的成员函数,修饰的是我们的this指针,准确来说是修饰的this指向的对象》
相当于修饰的是*this
我们看一下下面这一段代码:
c
lass Date
{
public:
Date(int year = 1998,int month = 1,int day = 1)
{
_year = year;
_month = month;
_day = day;
//cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
让程序运行一下:我们可以看到程序可以运行,但是下面我这样,
int main()
{
const Date d1;
d1.Print();
return 0;
}
程序会怎么样?
我们可以看到程序直接崩了。为什么?
我们可以知道,const修饰的常量不能被改变,那么我们用可以改变的常量去掉用它,那当然是崩了。如果我们还想去调用,该怎么办?
下来,我就说一说,看下面一段代码:
void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
const Date d1;
d1.Print();
return 0;
}
看一下运行的结果;
可以看出程序可以执行,
我们也可以这样调用;
void Print() const
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
const Date d1;
d1.Print();
Date d2;
d2.Print();
return 0;
}
看一下结果:
为什么,d2没有,const也可以调用函数?
答:在这里我们要明白这样一个道理,const可以读取,但是不能被修改,但是我d2过去,既可以读也可以写,所以可以调用函数,比如说:我们有很大权限,可以缩小自己的权限,但是不能去放大自己的权限。
——《取地址及const取地址操作符重载》——
其实这个就没有什么用了,非要说用哪里,那就看下面一段代码:
void Print() const
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
Date* operator&()
{
return this;
}
const Date* operator&() const
{
return this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
const Date d1;
d1.Print();
Date d2;
d2.Print();
cout << &d1 <<endl;
cout << &d2 <<endl;
return 0;
}
可以看到,我们都可以去const或者非const修饰的地址,如果我们不想比热容看到我们的地址,那么,我们可以看到以下的代码:
Date* operator&()
{
//return this;
return nullptr;
}
const Date* operator&() const
{
//return this;
return nullptr;
}
这样就可以看不到了。
总结一下:这两个默认成员函数一般不用重新定义 ,编译器默认会生成。这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比
如想让别人获取到指定的内容!
如果还没有明白,我推荐大家看一下他写的博客,我个人感觉写的很好,比我写的好,嘿嘿
https://blog.youkuaiyun.com/lihao21/article/details/8634876