如果每个人都能理解你,那你得普通成什么样子。
前言
这是我自己学习C++的第二篇博客总结。后期我会继续把C++学习笔记开源至博客上。
上一期笔记是关于C++的入门基础知识,没看的同学可以过去看看:
关于C++入门基础知识的笔记https://blog.csdn.net/hsy1603914691/article/details/142715432?spm=1001.2014.3001.5502
类的定义
类定义的格式
1. class为定义类的关键字。比如:定义一个栈结构,class为定义类的关键字,Stack为类的名字,{ }里面为类的主体,注意类定义结束时后面的 ; 不能省略。
2. 类的主体中的内容称为类的成员:类中的变量称为类的属性(成员变量);类中的函数称为类的方法(成员函数)。 3. 为了区分成员变量,成员变量会加⼀个特殊标识,如成员变量前面加上 ' _ ' 下划线。 4. C++中 struct 也可以定义类,C++兼容C中 struct 的用法,同时 struct 升级成了类,明显的变化是 struct 中可以定义函数,⼀般情况下我们还是推荐用 class 定义类。 5. 定义在类里面的成员函数默认为inline内联修饰函数,运行效率更高。class Stack{//类的主体};
访问限定符
1. C++⼀种实现封装的方式,就是用类将对象的属性(变量)与方法(函数)结合在⼀块,让对象更加完善,通过访问权限,选择性的将其接口提供给外部的用户使用。
2. public 修饰的成员在类外可以直接被访问;protected 和 private 修饰的成员在类外不能直接被访问,protected 和 private 本质作用是⼀样的。
3. 访问权限作用域从该访问限定符出现的位置开始,直到下⼀个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 ' } ',即类结束。
4. 一般来说,对象的属性(变量)是私有的,对象的方法(函数)是共有的。
5. class 定义类的时候,没有被访问限定符修饰的部分默认为 private,而 struct 定义类的时候,没有被访问限定符修饰的部分默认为 public。
6. 一般来说,我们通过类的函数来改变类的变量,使得行为更加规范可靠。
7. private 和 protected 定义的成员不能在类外使用,而public定义的成员可以在类外使用。
#include <iostream>using namespace std;class Stack{public:// 成员函数void Init(){}void Push(){}int Top(){}void Destroy(){}private:// 成员变量int* arr;size_t capacity;size_t top;};int main(){Stack st;//创建一个栈类型的变量,命名为stst.Init();//初始化stst.Push(1);//尾插stst.Push(2);//尾插stcout << st.Top() << endl;//打印st的首元素st.Destroy();//销毁streturn 0;}
类域
1. 类定义了⼀个新的作用域,类的所有成员都在类的作用域中,在类体外使用成员时,需要使用 :: (作用域操作符)指明成员属于哪个类域。
2. 当类域中的函数定义过长时候,一般在类域中存放函数声明(如果是缺省函数,那么函数声明时候写明缺省值),在类域外面定义函数。
3. 类域影响的是编译的查找规则,下面程序中 Init() 函数如果不指定类域 Stack,那么编译器就把 Init() 函数当成全局函数,那么在编译时,找不到函数中成员变量和成员函数的声明、定义在哪里,就会报错。
4. 指定类域 Stack,就是知道 Init() 函数是成员函数,当前域找不到函数中那些成员变量和成员函数,就会到类域中去查找。
#include<iostream>using namespace std;class Stack{public:// 成员函数void Init(int n = 4);private:// 成员变量int* arr;int capacity;int top;};// 声明和定义分离,需要指定类域void Stack::Init(int n)//指定类域Stack::Init,并且当存在缺省值时,只能在函数声明处加上形参的缺省值{//实现函数}int main(){Stack st;st.Init();return 0;}
实例化
实例化概念
1. 用类这种类型在物理内存中创建对象的过程,称为类实例化出对象。
2. 类是对象的⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。
3. ⼀个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
4. 类实例化出对象就像使用建筑设计图建造出房子,类就像是设计图,设计图规划了有多
少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房子才能住人。同样类就像设计图⼀样,不能存储数据,实例化出的对象才能分配物理内存,存储数据。对象大小
1. 类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量。但是成员函数并没有保存在每个对象里面。
2. 所以计算类的大小时候,只计算成员变量占用的内存,并不计算成员函数占用的内存。
3. C++规定类实例化的对象也要符合内存对齐的规则。
4. 如果是没有成员变量的类对象,则占一个字节,表示占位,但不存储有效数据。
#include<iostream>using namespace std;class A{public:void Print(){//...}private:char _ch;int _i;};class B{public:void Print(){//...}};class C{};int main(){A a;B b;C c;cout << sizeof(a) << endl;//8cout << sizeof(b) << endl;//1cout << sizeof(c) << endl;//1return 0;}
this指针
1. 编译器编译后,类的成员函数都会默认在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。
2. 类的成员函数访问成员变量,本质都是通过this指针访问的。
3. C++规定不能在实参和形参的位置的写this指针(编译时编译器会处理),但是可以在函数体内使用this指针。
#include <iostream>using namespace std;class Date{public:void Init(int year,int month,int day)//本质上是void Init(Date* this,int year,int month,int day)void Print()//本质上是void Print(Date* this)private:int _year;int _month;int _day;};int main(){Date d1;d1.Init(2024, 11, 3);//本质上是d1.Init(&d1, 2024, 11, 3)d1.Print();//本质上是d1.Print(&d1)Date d2;d2.Init(2024, 11, 4);//本质上是d2.Init(&d1, 2024, 11, 4)d2.Print();//本质上是d1.Print(&d2)}
封装特性
1. C++中数据和函数都放到了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++封装的⼀种体现,这个是最重要的变化。这里的封装的本质是⼀种更严格规范的管理,避免出现乱访问修改的问题。
2. C++中有⼀些相对方便的语法,比如 Init() 函数给的缺省参数会方便很多,成员函数每次不需要传对象地址,因为this指针隐含的传递了,方便了很多,使用类型不再需要 typedef ,用类名就很方便。
类的默认成员函数
1. 默认成员函数,就是即使用户没有写代码去实现,编译器也会自动生成的成员函数。
2. 对于⼀个类,一般情况下,编译器会生成六个默认成员函数。我们主要学习前面四个默认成员函数,对于后面两个默认成员函数只需了解即可。
构造函数
构造函数的理解
1. 构造函数是一个特殊的成员函数,其主要任务并不是开空间创建对象,而是对象实例化时自动调用来初始化对象。
2. 构造函数的本质是要替代我们以前写的 Init() 函数的功能,构造函数自动调用的特点就完美的替代的了手动调用 Init() 函数。
构造函数的特性
1. 构造函数的函数名与类名相同。
2. 构造函数无返回值。 (如果需要自己写构造函数,那么该函数不用返回任何值,也不需要在最前面写 void )
3. 构造函数可以实现重载。(无参数,有参数,缺省参数)
4. 对象实例化时,系统会自动调用对应的构造函数。
5. 如果类中没有自己定义构造函数,则C++编译器会自动生成⼀个无参的默认构造函数,⼀旦用户自己写代码实现构造函数,那么定义编译器将不再生成默认构造函数。
6. C++把变量类型分成内置类型和自定义类型。内置类型就是语言提供的原生数据类型,如:int、char、double、指针等,自定义类型就是我们使用class、struct等关键字自己定义的类型。
7. 对于内置类型成员变量,比如:Date、Stack,需要我们自己写代码去实现构造函数,这样才能在对象实例化中实现对象的初始化。
8. 对于自定义类型成员变量,比如:MyQueue,系统会自动调用默认构造函数初始化,不需要我们去管。
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}private:int _year;int _month;int _day;};int main(){Date d1;//在实例化对象d1的时候,会自动调用构造函数。d1.Print();//1 1 1return 0;}
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date()//无参数的构造函数{_year = 1;_month = 1;_day = 1;}Date(int year = 1, int month = 1, int day = 1)//全缺省的构造函数{_year = year;_month = month;_day = day;}Date(int year, int month, int day)//有参数的构造函数{_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}private:int _year;int _month;int _day;};int main(){Date d1(1, 1, 1);//在实例化对象d1的时候,会自动调用构造函数。至于调用哪种构造函数,则遵循函数重载。d1.Print();return 0;}
析构函数
析构函数的理解
1. 析构函数与构造函数功能相反,构造函数不是用来创建对象,析构函数也不是用来完成对象本身的销毁。 2. C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作。析构函数的特点
1. 析构函数的函数名就是在类名前加上字符 ~。
2. 析构函数无参数,无返回值。 (这里跟构造函数类似,也不需要在最前面加 void )
3. ⼀个类只能有⼀个析构函数。若用户没有自己写代码实现,则系统会自动生成默认的析构函数。
4. 对象生命周期结束时,系统会自动调用析构函数。
5. 跟构造函数类似,编译器自动生成的析构函数,对内置类型成员不做处理,对自定类型成员会调用系统的析构函数。
6. 即使我们自己写析构函数,对于自定义类型成员也会调用系统的析构函数,也就是说自定义类型成员无论什么情况都会自动调用系统的析构函数。
7. 对于内置类型成员变量,如果类中没有申请资源时,那么析构函数可以不写,可以直接使用编译器生成的默认析构函数,如Date类;对于自定义类型成员变量,也就不需要自己写析构函数,如MyQueue类;但是除此之外,有资源申请时,⼀定要自己写析构函数,否则会造成资源泄漏,如Stack类。
8. ⼀个局部域的多个对象,C++规定后定义的对象先析构。
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}~Date(){cout << '1' << endl;}private:int _year;int _month;int _day;};int main(){Date d1(1, 1, 1);//在实例化对象d1的时候,自动调用无参的默认构造函数。d1.Print();return 0;//d1的生命周期结束时,会自动销毁,此时会自动调用析构函数,清理内存资源空间}
拷贝构造函数
拷贝构造函数的理解
1. 如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。
拷贝构造函数的特性
1. 拷贝构造函数是构造函数的⼀个重载。
2. 拷贝构造函数的第⼀个参数必须是自身类类型对象的引用。(如果使用传值传参方式,编译器直接报错,因为语法逻辑上会引发无穷递归调用)
3. 拷贝构造函数也可以多个参数,但是第⼀个参数必须是自身类类型对象的引用,后面的参数必须有缺省值。
4. C++规定自定义类型对象进行拷贝行为时,自动调用拷贝构造函数。
5. 若用户没有自己写代码定义拷贝构造函数,编译器会自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝、浅拷贝(⼀个字节⼀个字节的拷贝),比如说Date类型;对自定义类型成员变量会调用系统的拷贝构造函数,比如说MyQueue类型;但是对于有动态内存扩增的内置类型成员变量,需要我们自己去写拷贝构造函数(叫做深拷贝),比如说Stack类型。
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(int year, int month, int day){_year = year;_month = month;_day = day;}Date(const Date& d)//隐藏了this指针{_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}~Date(){cout << '1' << endl;}private:int _year;int _month;int _day;};int main(){Date d1(1, 1, 1);//在实例化对象d1的时候,直接调用有参数的默认构造函数。d1.Print();Date d2(d1);//自动调用拷贝构造函数。构造函数存在重载,由于参数不同,故系统通过参数来判需要自动调用的是哪一个构造函数。Date d3 = d1;//拷贝构造函数的另一种调用形式。d2.Print();d3.Print();return 0;//d1、d2、d3的生命周期结束时,会自动销毁,此时会自动调用析构函数,清理内存资源空间}
赋值运算符重载函数
一般运算符重载
1. 当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须调用对应运算符重载函数,若没有对应的运算符重载函数,则会编译报错。
2. 运算符重载函数是具有特殊名字的函数,它的名字是由operator和后面要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
3. 重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多。⼀元运算符有⼀个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第二个参数。
4. 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。
5. 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
6. 不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
7. ' .* ' , ' :: ' , ' sizeof ' , ' ? : ' , ' . ' 注意以上5个运算符不能重载。
8. 重载操作符至少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: 不能出现 int operator+(int x, int y)。
9. ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如:Date类重载operator-有意义,但是重载operator*就没有意义。
10. 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
11. 重载<<和>>时,需要重载为全局函数。因为如果重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调用时就变成了 " 对象<<cout ",不符合使用习惯和可读性。如果重载为全局函数,把ostream、istream放到第⼀个形参位置就可以了,第二个形参位置当类类型对象。(日期类实现中用到了)
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << " " << _month << " " << _day << endl;}~Date(){cout << 1 << endl;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}booloperator<(const Date& d)// 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。{if (_year < d._year){return true;}else if ((_year < d._year) && (_month < d._month)){return true;}else if ((_year < d._year) && (_month < d._month) && (_day < d._day)){return true;}else{return false;}}//++d1Date operator++(){*this += 1;return *this;}//d1++Date operator++(int i){Date dn(*this);//创建中间变量dn,并且自动调用拷贝函数,拷贝*this变量的内容。*this += 1;return dn;}private:int _year;int _month;int _day;};int main(){Date d1;//创建类变量时候自动调用无参构造函数d1.Print();Date d2(d1);//自动调用拷贝构造函数d2.Print();bool ret = d1.operator<(d2);cout << ret << endl;return 0;//销毁类变量时候自动调用析构函数}
赋值运算符重载
1. 赋值运算符重载是⼀个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于⼀个对象拷贝给另⼀个刚刚创建的对象。
2. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成 const 当前类类型引用,否则会传值传参会有拷贝。(一般类类型都使用传地址而非传值,避免进入死循环,而传地址时更推荐用引用&,而为了避免发生权限放大,我们在形参前面加上 const 限定,所以一般是 const Date& d )
3. 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。
4. 没有写代码去实现赋值运算符重载时,编译器会自动生成⼀个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝、浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数。
5. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显⽰实现赋值运算符重载;像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝、浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝;像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调⽤Stack的赋值运算符重载,也不需要我们显示实现MyQueue的赋值运算符重载。
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date()//无参数的构造函数{_year = 1;_month = 1;_day = 1;}Date(int year = 1, int month = 1, int day = 1)//全缺省的构造函数{_year = year;_month = month;_day = day;}//Date(int year, int month, int day)//有参数的构造函数//{//_year = year;//_month = month;//_day = day;//}void Print(){cout << _year << " " << _month << " " << _day << endl;}~Date(){cout << 1 << endl;}Date(const Date& d)//隐藏了this指针{_year = d._year;_month = d._month;_day = d._day;}booloperator<(const Date& d)// 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。{if (_year < d._year){return true;}else if ((_year < d._year) && (_month < d._month)){return true;}else if ((_year < d._year) && (_month < d._month) && (_day < d._day)){return true;}else{return false;}}//++d1//Date operator++()//{//*this += 1;//return *this;//}//d1++//Date operator++(int i)//{//Date dn(*this);//创建中间变量dn,并且自动调用拷贝函数,拷贝*this变量的内容。//*this += 1;//return dn;//}Date& operator=(const Date& d)//隐藏了this指针{_year = d._year;_month = d._month;_day = d._day;return *this;}private:int _year;int _month;int _day;};int main(){//Date d1;//创建类变量时候自动调用构造函数//d1.Print();//Date d2(d1);//效用拷贝构造函数//d2.Print();//bool ret = d1.operator<(d2);//cout << ret << endl;Date d1(1,1,1);d1.Print();Date d2 = d1;d2.Print();Date d3(2, 2, 2);d3.Print();d3 = d1;d3.Print();return 0;//销毁类变量时候自动调用析构函数}
总结:
1. 构造函数:对于内置类型,需要自己写代码实现构造函数 。
2. 析构函数、拷贝构造函数、赋值运算符重载函数:对于有资源申请的内置类型,需要自己写代码实现这些函数。
3. 运算符重载函数:大部分情况需要自己写代码实现运算符重载函数。
日期类的实现
<date.h>文件
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);Date(int year = 1, int month = 1, int day = 1)//内置类型必须自己实现构造函数{_year = year;_month = month;_day = day;}bool operator<(const Date& d);bool operator>(const Date& d);bool operator=(const Date& d);bool operator<=(const Date& d);bool operator>=(const Date & d);bool operator!=(const Date& d);int get_month_day();Date& operator+=(int day);Date& operator+(int day);Date& operator-=(int day);Date& operator-(int day);Date& operator++();//前置++Date& operator++(int i);//后置++void Print();int operator-(const Date& d);private:int _year;int _month;int _day;};
<date.cpp>文件
#include "date.h"bool Date::operator<(const Date& d){if (_year < d._year){return true;}else if ((_year == d._year) && (_month < d._month)){return true;}else if ((_year == d._year) && (_month == d._month) && (_day < d._day)){return true;}else{return false;}}bool Date::operator>(const Date& d){return !(*this <= d);}bool Date::operator=(const Date& d){if ((_year == d._year) && (_month == d._month) && (_day == d._day)){return true;}else{return false;}}bool Date::operator<=(const Date& d){return (*this = d) && (*this < d);}bool Date::operator>=(const Date& d){return !(*this < d);}bool Date::operator!=(const Date& d){return !(*this = d);}int Date::get_month_day(){static int month[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (((_year % 4 == 0) && (_year % 100 != 0))|| (_year % 400 == 0)){if (_month == 2){return 29;}else{return month[_month];}}else{return month[_month];}}Date& Date::operator+=(int day){if (day < 0){return *this -= -day;}_day = day + _day;while (_day> (*this).get_month_day()){_day = _day - (*this).get_month_day();_month++;if (_month == 13){_year++;_month = 1;}}return *this;}Date& Date::operator+(int day){Date newdate(*this);if (day < 0){return newdate -= -day;}newdate._day += day;while (newdate._day > newdate.get_month_day()){newdate._day -= newdate.get_month_day();newdate._month++;if (newdate._month == 13){newdate._year++;newdate._month = 1;}}return newdate;}//date& date::operator+=(int day)//{//*this = *this + day;//return *this;//}Date& Date::operator-=(int day){if (day < 0){return *this += -day;}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year -= 1;_month = 12;}_day += (*this).get_month_day();}return (*this);}Date& Date::operator-(int day){Date newdate(*this);if (day < 0){return newdate += -day;}newdate._day -= day;while (newdate._day <= 0){newdate._month--;if (newdate._month == 0){newdate._year -= 1;newdate._month = 12;}newdate._day += (*this).get_month_day();}return newdate;}Date& Date::operator++()//前置++{*this += 1;return (*this);}Date& Date::operator++(int i)//后置++{Date newdate(*this);*this += 1;return (newdate);}void Date::Print(){cout << " " << _year << " " << _month << " " << _day << " " << endl;}int Date::operator-(const Date& d){Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;n++;}return n * flag;}ostream& operator<< (ostream& out, const Date& d){out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;}istream& operator>> (istream& in, Date& d){cout << "请输入年月日:";in >> d._year >> d._month >> d._day;return in;}
<test.cpp>文件
#include "date.h"int main(){//Date d1;//d1.Print();//1 1 1//d1 += 31;//d1.Print();//1 2 1//Date d2 = (d1 + 1);//d1.Print();//1 2 1//d2.Print();//1 2 2//d2 -= 1;//d2.Print();//1 2 1//d2 -= 31;//d2.Print();//1 1 1//Date d3=d2++;//d3.Print();//1 1 1//d2.Print();//1 1 2//Date d4=++d2;//d4.Print();//1 1 3//d2.Print();//1 1 3Date d1(1, 1, 1);Date d2(8, 5, 5);cout << d1;operator<<(cout, d1);cin >> d1 >> d2;cout << d1 << d2;return 0;}
取地址运算符重载
const成员函数
1. 将const修饰的成员函数称之为const成员函数,const修饰成员函数时,需要把const放到成员函数参数列表的后面。
2. const实际上修饰的是该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
3. const修饰指针本身的时候,不能改变指针指向的对象,但是可改变指针指向的内容;const修饰指针指向的内容的时候,可以改变指针指向的对象。其中,后者才会触发权限的放大和缩小。
4. 如果我们要求改该成员变量,那么就不能加const,也不能调用const修饰的成员函数;如果该成员函数不改变成员变量,那么就建议都加const修饰该成员函数。
#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;class Date{public:Date(int year = 1, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}void Print() const{cout << _year << " " << _month << " " << _day << endl;}//void Print(Date* const this) //虽然有const修饰,但是修饰的是指针本身,即this指针只能指d2,不能改变指向的对象,但可以改变指针的内容//{//cout << _year << " " << _month << " " << _day << endl;//}//void Print(const Date* const this) const//{//cout << _year << " " << _month << " " << _day << endl;//}private:int _year;int _month;int _day;};int main(){Date d1(5, 5, 5);const Date d2(4, 4, 4);//此时d2的指针是 const Date* d2; 即d2的数值不能进行修改d1.Print();d2.Print();}
取地址运算符重载
1. 取地址运算符重载,分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。
致谢
感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能!