网站制作内容文案,招商外包服务公司,jsp 网站连接数据库,做外贸要做什么网站类的6个默认成员函数 如果一个类中什么成员都没有#xff0c;简称为空类。空类中什么都没有吗#xff1f;并不是的#xff0c;任何一个类在我们不写的情 况下#xff0c;都会自动生成下面6个默认成员函数。 构造函数#xff1a;
定义#xff1a;构造函数是一个特殊的成员…类的6个默认成员函数 如果一个类中什么成员都没有简称为空类。空类中什么都没有吗并不是的任何一个类在我们不写的情 况下都会自动生成下面6个默认成员函数。 构造函数
定义构造函数是一个特殊的成员函数名字与类名相同,创建类 类型对象时由编译器自动调用保证每个数据成员都有 一个合适的初始值并且在对象的生命周期内只调用一次。 特性
1.不是开空间创建对象而是初始化对象。
2. 函数名与类名相同。
3. 无返回值。
4. 对象实例化时编译器自动调用对应的构造函数。
5. 构造函数可以重载。 #include iostream
#include assert.husing namespace std;class Date
{
private:int _year;int _month;int _day;public://1.无参构造函数Date(){}// 2.带参构造函数Date(int year, int month, int day){_year year;_month month;_day day;}
};int main()
{Date d1; // 调用无参构造函数Date d2(2015, 1, 1); // 调用带参的构造函数// 注意如果通过无参构造函数创建对象时对象后面不用跟括号否则就成了函数声明// 以下代码的函数声明了d3函数该函数无参返回一个日期类型的对象Date d3();return 0;
}
根据不同的初始化需求去选择构造函数 6.如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。
class Date
{
private:int _year;int _month;int _day;void MPrintf(){cout _year 年 _month 月 _day 日 endl;}
};int main()
{
// 没有定义构造函数对象也可以创建成功因此此处调用的是编译器生成的默认构造函数Date d1;d1.MPrintf();return 0;
} 7.无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。注意无参 构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数都可以认为是默认成员函数。 默认成员函数分为 为什么默认构造函数只能有一个
回答
当同时有无参构造函数和全缺省构造函数时在实例化过程中编译器无法判断选择哪一种构造函数。
//为什么默认构造函数只能有一个
class Date
{
private:int _year;int _month;int _day;public://1.无参构造函数Date(){}// 2.全缺省构造函数Date(int year2018, int month1, int day1){_year year;_month month;_day day;}void MPrintf(){cout _year 年 _month 月 _day 日 endl;}
};int main()
{ // 调用无参构造函数Date d1; //全缺省构造函数--注释1.无参构造函数Date d2(1111);d2.MPrintf();Date d3(2222, 2);d3.MPrintf();Date d4(3333, 3, 3);d4.MPrintf();return 0;
}此时编译器是无法确定选择哪一种默认构造函数
全缺省构造函数结果 8.默认构造函数多用于自定义类型
对于自定义类型复杂情况我们会用构造函数的默认生成更方便
class Time
{
public:
//默认构造Time(){cout Time() endl;_hour 0;_minute 0;_second 0;}
private:int _hour;int _minute;int _second;
};
class Date
{private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}
调试结果 对于内置类型默认构造函数时不进行内容的改变只保留随机值。
对于自定义类型我们设置了他的默认成员函数随机值进行了改变在以后多次使用tmie类型是他的初始化内容都会改变成自己一开始设置的初始化内容。 内置和自定义混合的可以给内置缺省 此处不是初始化空间没有创造就不能算是初始化只能算是声明缺省值 解释默认构造没有参数传递使用其原本的缺省值
由此得出
默认生成构造函数
1.内置类型成员不做处理
2.自定义类型成员会去调用它的默认构造不用传参数的构造 建议每个类都提供一个默认构造函数内置类型-缺省构造 析构函数
定义与构造函数功能相反析构函数不是完成对象的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成类的一些资源清理工作。 特征
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义系统会自动生成默认的析构函数。
4. 对象生命周期结束时C编译系统系统自动调用析构函数。 拷贝构造函数
定义只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。将已近实例化好的类型对象拷贝给将要实例化的新的对象的构造函数
特征
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须使用引用传参使用传值方式会引发无穷递归调用。 为什么只传一个参数
回答其在结构体内部本身还有一个隐形的This指针。 必须使用引用的原因
回答避免出现使用传值方式会引发无穷递归调用。--在拷贝时先准备启用拷贝构造函数传值时会调用拷贝构造函数。这样会形成无穷递归调用。不理解直接记成拷贝构造函数必用引用 使用const的原因
回答在传递过程中将可读可写的修改成const只读模式。将其权限缩小避免因为自己思路的问题而导致原本值的改变。 3. 若未显示定义系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝我们叫做浅拷贝或者值拷贝。 代码
#include iostreamusing namespace std;class Date
{
public://构造函数-全缺省Date(int year 2018, int month 1, int day 1){_year year;_month month;_day day;}拷贝构造函数//Date(const Date d)//{// _year d._year;// _month d._month;// _day d._day;//}void Mprintf(){cout _year / _month/ _day endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Mprintf();Date d2(d1);d2.Mprintf();return 0;
}结果 通过两次运行结果对比发现貌似这个拷贝构造函数有无都一样
其实不然C语言本身是对一些内置类型int char等在编译器的底层是有类似拷贝构造函数的结构的。
但是面对一些自定义类型或者申请有申请空间的情况C语言不能满足需求
那就需要我们自己写专门针对自己的类的类型的拷贝构造函数
如以下代码
//开辟一个字符串空间
class String
{
public:String(const char* str jack){_str (char*)malloc(strlen(str) 1);strcpy(_str, str);}//析构函数~String(){cout ~String() endl;free(_str);}
private:char* _str;
};
int main()
{String s1(hello);String s2(s1);
}此时拷贝的运行结果就是错误。 什么情况下用拷贝构造函数 自己实现了析构函数释放空间就需要实现拷贝构造函数 拷贝构造应用场景
获取x天后的日期
//拷贝构造应用场景
class Date
{
public://构造函数--全缺省Date(int year 2018, int month 1, int day 1){_year year;_month month;_day day;}//打印函数void Mprintf(){cout _year / _month / _day endl;}int GetMonthDay(int year,int month){assert(month 0 month 13);//正常情况下当前月数返回的天数月从1开始所以多加一位占位int monthArray[13] { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//闰年2月天数情况if (month 2 ((year % 4 0 year % 100 ! 0) || (year % 400) 0)){return 29;}else{return monthArray[month];}}//x天后的日期Date GetAfterXDay(int x){//因为不想改变初始日期所以使用拷贝Date tmp *this;tmp._day x;while (tmp._day GetMonthDay(tmp._year, tmp._month)){tmp._day - GetMonthDay(tmp._year, tmp._month);tmp._month;if (tmp._month 13){tmp._year;tmp._month 1;}}return tmp;}private:int _year;int _month;int _day;
};int main()
{Date d1(2222, 2, 2);//d1.GetAfterXDay(100)调用类d1里的GetAfterXDay函数//返回一个Date类型的对象再拷贝到Date d2返回值d2中Date d2 d1.GetAfterXDay(100);d1.Mprintf();d2.Mprintf();return 0;
} 赋值运算符重载
运算符重载
C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其返回值类 型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。
函数名字为关键字operator后面接需要重载的运算符符号。
函数原型返回值类型 operator操作符(参数列表)对于比较类不需要更改值常用const修饰
用于内置类型的操作符其含义不能改变例如内置的整型不 能改变其含义
.* 、:: 、sizeof 、?: 三目运算符、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
公共成员
要在类外调用还得将声明对象的private改成public
class Date
{
public:Date(int year 1111, int month 1, int day 1){_year year;_month month;_day day;}//private:int _year;int _month;int _day;
};
//d1与d2位置分别对应的两端不能修改
//参数和操作数函数形参位置是成正比的
bool operator(const Date d1, const Date d2)
{return d1._year d2._year d1._month d2._month d1._day d2._day;
}
int main()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout (d1 d2) endl;//d1d2会自动call对于函数地址//改变成-operator(d1,d2)进行比较
}
为什么要在输出是加括号
回答因为 和 优先级不同cout d1 d2 endl;只会先识别cout d1 成员函数
class Date
{
public:Date(int year 1111, int month 1, int day 1){_year year;_month month;_day day;}
//因为是成员函数所以有其自身的this
// 完全展开后bool operator(Date* this, const Date d2)bool operator(const Date d2){return _year d2._year _month d2._month _day d2._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);d1 d2;//因为是成员函数其内部自身有隐藏this所以值传递一个值d1.operator(d2);cout (d1 d2) endl;cout d1.operator(d2) endl;
}
并不局限于除了.* 、 :: 、 sizeof 、 ?: 三目运算符 、 . 这5个外都可以用operator运算符更改
运算符重载的复用
代码
//运算符重载的复用
class Date
{
public:Date(int year 1111, int month 1, int day 1){_year year;_month month;_day day;}//因为是成员函数所以有其自身的this// 完全展开后bool operator(Date* this, const Date d2)bool operator(const Date d2){return _year d2._year _month d2._month _day d2._day;}//复用//由于已知了根据就能写出 小于或等于同理bool operator!(const Date d){return !(*this d);//相等1再-false//不等0再-true}
private:int _year;int _month;int _day;
};int main()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout (d1!d2) endl;//显示1truereturn 0;
}赋值运算符重载
主要特点
1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值
4. 返回*this
5. 一个类如果没有显式定义赋值运算符重载编译器也会生成一个完成对象按字节序的值拷贝。
赋值运算使用
常用域自己实现了析构函数释放空间就需要使用自己编写的赋值运算符重载 class Date
{
public:Date(int year 1111, int month 1, int day 1){_year year;_month month;_day day;}void Mprintf(){cout _year / _month/ _day endl;}
private:int _year;int _month;int _day;
};class String
{
public://拷贝String(const char* str ){_str (char*)malloc(strlen(str) 1);strcpy(_str, str);}//析构~String(){cout ~String() endl;free(_str);}void Mprintf(){cout _str endl;}
private:char* _str;
};int main()
{Date d1(2222, 9, 26);Date d2(3333, 9, 27);String s1(hello);String s2(world);d1.Mprintf();d2.Mprintf();s1.Mprintf();s2.Mprintf();//编译器生成的默认赋值重载函数已经可以完成字节序的值拷贝了d1 d2;s1 s2;//报错//此时自己没有写赋值重载函数当s2赋值给s1后//s1和s2同时指向相同的一块空间s2开出的//后序清理资源清理s2的时候s1所指向的空间也会改变//所以错误编译器不通过d1.Mprintf();s1.Mprintf();return 0;
}
连续赋值
有返回值用于支持这里的连续赋值保持运算符的特性
eg.ijk连续赋值的顺序是先jk后返回j再ij
所以由于以上连续赋值的可能性也要考虑赋值运算符重载的连续赋值的可能。 class Date
{
public:Date(int year 1111, int month 1, int day 1){_year year;_month month;_day day;}void Mprintf(){cout _year / _month/ _day endl;}//d3d2d1//返回的是赋值的左操作数所以返回*this//返回后该操作数的生命周期还存在所以用引用返回Date operator(const Date d){_year d._year;_month d._month;_day d._day;return *this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(1111, 1, 1);Date d2(2222, 2, 2);Date d3(3333, 3, 3);d1.Mprintf();//1111,1,1d1 d2 d3;d1.Mprintf();//3333,3,3return 0;
}
自己给自己赋值
有时不小心的操作可能会造成自己给自己赋值的情况
所以还需要在自己写的赋值情况中加入if判断 总结
赋值重载和拷贝构造区别
赋值重载是多个已近定义出来的对象
拷贝构造是一个已经实例化的对象初始化另一个未实例化的对象 在使用引用返回时不能盲目的为了追求使用引用返回而使用静态变量static
静态变量static在整体函数调用中只会初始化他的第一次再次走到static内一步时不会再初始化而是使用其之前的值