类与对象
c++发展中形成的优势和出现的问题:
1.c++在c的基础上加入了面向对象的特性;实现高内聚低耦合;
2.c++封装的特性,使得数据更加安全;
3.某些参数不需要显示传递,可以让编译器完成,显得更加灵活;
4.相较于以前写数据结构,不用老是出现忘记初始化和释放空间的问题;c++在创建对象后会自动调用构造函数,进行初始化,而在对象生命周期将结束时,先自动调用析构函数,然后释放空间,不用显示写。对于内置类型,calloc,malloc都不会自动生成并调用构造函数,需要显式地写进构造函数;
5.初始化自定义类型时,成员有自定义类型,想要给自定义类型的自定义类型成员进行带参构造,直接写构造实现不了,还需要使用初始化列表;
6.现在内置类型和自定义类型都支持 对象名+(参数) 来初始化,初始化列表是所有成员变量初始化的地方;
7.深拷贝和大对象拷贝代价太大,所以产生了引用;
8.为了增加代码可读性,出现了运算符重载;
9.为了解决深拷贝等问题,出现了new 和delete;
10.随着自定义类型的出现,printf已经无法满足需求,所以出现了流插入和流提取和运算符重载;
class B
{
public:
B(int x, int y) { a = x; b = y; }
private:
int a;
int b;
};
class A
{
public:
//A() //无参构造
//{
// cout << "无参构造" << endl;
//}
A(int a = 10, int b = 20, int c = 30, int d = 30):b1(a,b),b2(c,d)
{
//这样不可以
/*b1(a, b);
b2(c, d);*/
}
private:
B b1;
B b2;
};
int main()
{
int a(10);//对内置类型的初始化
return 0;
}
1.面向过程与面向对象的认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题,而C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
面向过程是将一个复杂问题简单化,分步完成。
面向对象则关注的是将现实世界进行计算机世界建模。为了更好地管理这个世界,先进行描述和组织的封装,然后对于某一类事务的属性进行特征提取、描述,如:学生老师等不同的身份,具体的姓名、年龄、地址等;然后组织好所有事务,呈现出有序性,提高效率,如:学生之间的管理,老师之间的管理,具体有学生信息的录入,课程信息的添加,老师信息的添加等都用一定的结构(数据结构)维护好,这样就使得人们不用在现实生活中面对面进行交互,而是通过计算机世界建模完成不同角色的任务,解决现实生活中人群拥挤问题,效率不高问题;并且不再像面向过程一样串行逻辑,而是可以同时执行不同任务。
面向对象这种开发思想,其实就是来源于Unix和类Unix操作系统内核的部分设计,在工程上,这种设计弱化了底层的差异,隐藏了底层的实现细节,实现了上层的统一,更加符合高内聚低耦合特性。
2.类的引入
c++中将struct结构体升级成了类并且兼容以前的用法。
2.1类的定义
class className
{
// 类体:由成员函数和成员变量组成
};//将成员变量和成员函数放到了一起
//1.对于成员函数的定义,不同类域可以定义同名函数,用c写不同数据结构都得写不同前缀来区分不同结构类似功能,如:stackinit(),queueinit(),而现在可以实现统一;
//2.对于放在同一类域的内容,认为是一个整体,没有先后之分;
成员函数的定义可以声明和定义分离,类放声明,定义前指定类域;不分离,成员函数默认就是内联。
2.1.1成员变量命名规则的建议
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
2.2类的访问限定符和封装
2.2.1访问限定符
访问限定符:public(公有)、private(私有)、protected(保护);访问限定符仅仅在编译阶段有效。
【访问限定符说明】
\1. public修饰的成员在类外可以直接被访问
\2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
\3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
\4. 如果后面没有访问限定符,作用域就到 } 即类结束。
\5. class的默认访问权限为private,struct为public(因为struct要兼容C)
2.2.2面向对象三大特性:封装
面向对象三大特性包括,封装、继承、多态;
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。 封装是为了更好地管理,隐藏底层细节可以更好地保护,如:现实世界中对于名胜古迹的保护,防止遗迹本身被破坏,还有操作系统提供系统调用接口供上层使用,而内核数据不能让上层访问,防止被恶意修改,导致操作系统损坏等,都是这类思想。即c语言没有禁止错误使用方式,而c++设置了权限。
struct stack
{
void push(int x)
{
a[top++]=x;
capacity++;
}
int *a;
int top;
int capacity;
};
int main()
{
stack st;
//使用函数push数据
st.push(1);
//使用成员变量修改,严禁此操作
a[top++]=1;
capacity++;
//其他人使用成员变量破坏
a[top]=0;
return 0;
}
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其
接口提供给外部的用户使用。
2.3类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中**。**在类体外定义成员时,需要使用 :: 作用域操作
符指明成员属于哪个类域。
只有局部域和全局域会影响生命周期。函数具体的实现是定义,变量开辟了空间才有定义。类不会单个成员变量定义,而是整体定义,即一起开空间。
2.4类的实例化
用类类型创建对象的过程,称为类的实例化;定义类并不会开空间;一个类实例化之后才会占据空间。所以类一般都是用对象去访问。
2.5类的模型大小
类按照内存对齐,类的大小只有成员变量,不包括成员函数,所以函数不放在对象里,而是放在公共代码区域。
class A1
{
public:
void init(){}//所有类对象的通用方法,一份就行,使用的都是同一种方法
private:
int a;//类对象的成员变量,不同对象变量一般不同
}
class A2//如:仿函数
{
public:
void test(){}
private:
};
class A3//空类
{
public:
private:
};
int main()
{
cout << sizeof(A1) << endl;//4
//没有成员变量的类需要1字节来占位,表示对象存在
//不存储有效数据
cout << sizeof(A2) << endl;//1
cout << sizeof(A3) << endl;//1
return 0;
}
2.5.1结构体内存对齐规则
\1. 第一个成员在与结构体偏移量为0的地址处。
\2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
\3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
\4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
为什么要内存对齐?
主要因为,计算机是32/64位的,每次读取到的数据是固定大小的,为了提高读取效率,减少读取次数,所以以空间换时间进行内存对齐。
2.6this指针
2.6.1this指针的使用和注意事项
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。this不能在形参和实参显示传递,可在函数内部使用或函数内部调用函数传递,但是只能读
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
/* void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}*/
void Print(Data* const this)//经过编译器编译变成这样
{
cout <<this->_year<< "-" <<this->_month << "-"<< this->_day <<endl;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
int a;
};
int main()
{
Date d1, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();//Print(&d1);
d2.Print();
return 0;
}
this指针存放在哪里?this指针是形参存放在栈区。先进行压栈,参数从右往左传;
只有对空指针进行解引用才会报错,传递不会;不能读取和修改空指针;
2.7c++和c语言实现数据结构
本质上区别不大,底层转成汇编是一样的,只不过c++编译器多进行了一些处理。
2.8对象的生命周期
对象先实例化,在自动调用构造函数,出了作用域自动调用析构函数,然后对象销毁。
3.类的6大默认成员函数
如果一个类中什么成员都没有,简称为空类;空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。包括:构造、析构、拷贝构造、赋值重载、普通对象取地址、const对象取地址;
c++兼容c语言,对内置类型不初始化,不清理,浅拷贝;
3.1构造函数
构造函数并不是用来创建对象的,而是用于初始化。对象实例化才是创建对象,在main函数栈帧中创建。
3.1.1概念
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。初始化并不仅仅是对成员变量的一些设置,也可以利用其自动调用的特性来实现某些功能。
3.1.2特性
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开
空间创建对象,而是初始化对象。
其特征如下:
1.函数名与类名相同;
2.无返回值;
3.对象实例化时编译器自动调用对应的构造函数;
4.构造函数可以重载,可以实现多种初始化的方式;
5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。即只要写了构造函数,编译器就不会生成了;
默认构造包括:无参构造、全缺省构造、编译器自动生成的构造。无参和全缺省构成函数重载,但是默认构造函数只能有一个,会产生调用歧义,可以无参调用就是默认构造函数。
class A
{
public:
// A(){}//无参构造
A(int a=10,int b=20,int c=30){
a_=a;
b_=b;
c_=c;
}//全缺省构造
private:
int a_;
int b_;
int c_;
};
int main()
{
A a;//调用无参构造
//A a1();//不会创建对象,会识别成函数声明
return 0;
}
编译器生成的默认构造,c++标准中,对于内置类型(指针也是内置类型)不做处理,原来是什么就还是什么,并没有初始化成0,而对于自定义类型会调用自定义类型的默认构造,但是有的编译器初始化成0。建议有内置类型就自己写构造函数。针对内置类型不做处理,c++11打了一个补丁,支持成员变量在声明的时候可以给缺省值,这缺省值是编译器用来生成默认构造,传给初始化列表用的。而有缺省值且用static修饰就必须用const修饰。
3.2析构函数
3.2.1概念
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
3.2.2特性
析构函数是特殊的成员函数,其特征如下:
1.析构函数名是在类名前加上字符 ~;
2.无参数无返回值类型;
3.一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载;
4.对象生命周期结束时,C++编译系统系统自动调用析构函数;
5.编译器自动生成的析构函数,对内置类型不做处理,自定义类型会自动调用其析构函数, 内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;
6.析构顺序是后进先析构,因为后面的对象可能正在使用前面的对象,先析构前面的对象会导致后面的对象无法正常使用;
7.同一块对空间是不能释放两次的,如果free(void *ptr)中ptr为空,则不会产生任何操作;
3.3拷贝构造函数
3.3.1概念
拷贝构造函数**:**只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
3.3.2特性
拷贝构造函数也是特殊的成员函数,其特征如下:
1.拷贝构造函数是构造函数的一个重载形式;
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用,传值传参会产生临时对象,临时对象是实参的拷贝,会调用该类型拷贝构造。因为是引用,要防止对原对象造成破坏,或者实参用const修饰了,而权限不能放大,所以要用const接收;
3.c++11规定了,内置类型浅拷贝就能满足要求,而自定义类型拷贝必须要调用拷贝构造函数完成,因为浅拷贝不会自动调用malloc,而是浅拷贝了内置类型指针,该指针指向了堆上开辟的空间。这样会产生危害,如:1.若进行析构,两个对象会对同一块堆空间进行释放,导致程序崩溃;2.一个对象对堆空间的值进行了修改,另一个对象也发生了改变,所以需要自己写拷贝构造来实现深拷贝;
4.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成
拷贝,这种拷贝叫做浅拷贝,或者值拷贝。引用传参/或返回减少深拷贝,但要注意析构两次的问题,局部变量返回时,会有右值引用来解决效率问题;
3.4赋值运算符重载
3.4.1运算符重载
运算符重载并不会改变原来的优先级和结合性
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 函数名字为:关键字operator后面接需要重载的运算符符号。函数原型:返回值类型operator操作符**(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@;
2.重载操作符必须有一个类类型参数;
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义;
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this;
5.“ .* :: sizeof ?: . ” 注意以上5个运算符不能重载;
6.运算符重载时要注意,原来操作符有几个操作数,重载函数就有几个参数;
#include<iostream>
#include<cstdlib>
#include<string>
using namespace std;
class Date
{
public:
Date(int year = 2024, int month = 1, int day = 1)
{
year_ = year;
month_ = month;
day_ = day;
}
public:
int year_;
int month_;
int day_;
};
bool operator<(const Date& x1, const Date& x2)
{
if (x1.year_ < x2.year_)
{
return true;
}
else if (x1.year_ == x2.year_ && x1.month_ < x2.month_)
{
return true;
}
else if (x1.year_ == x2.year_ && x1.month_ == x2.month_ && x1.day_ < x2.day_)
{
return true;
}
return false;
}
string btos(bool args)
{
if (args)
{
return "true";
}
else
{
return "false";
}
}
//类外函数使用类内成员,可以用友元函数来突破封装,但是友元函数能不用就不用
int main()
{
Date d1(2021, 1, 1);
Date d2(2021, 1, 1);
cout << btos(Less(d1, d2)) << endl;
cout << btos(d1 < d2) << endl;
cout << operator<(d1 < d2) << endl;
return 0;
}
3.4.2赋值运算符重载和拷贝构造的区别
赋值运算符重载–运算符重载函数,用来拷贝赋值,而拷贝构造函数–构造函数,用来初始化;
所以初始化只能一次,防止和赋值冲突。
int main()
{
A aa(1);
const A aa1 = 1;//初始化
//aa1 = aa;//赋值不能const修饰
int a = 1;
const int b = a;//初始化转换成了const int b(a)
//b = a;//赋值
return 0;
}
为了能够实现连等
Date& operator=(const Date& x)
{
year_ = x.year_;
month_ = x.month_;
day_ = x.day_;
return *this;
}
3.4.3特性
1.赋值运算符重载格式 ,参数类型:const T&,传递引用可以提高传参效率, 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值,返回 this :要符合连续赋值的含义;
2.赋值运算符只能重载成类的成员函数不能重载成全局函数;
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值;
3.4.4日期类的实现
1.前置++和后置++,为了实现函数重载,后置++需要给一个额外参数int声明;
2.学会使用复用逻辑;
class Date//1.复用+=实现+,拷贝2次,而复用+实现+=,拷贝4次;2.前置++无需拷贝,而后置++拷贝2次
{
friend ostream& operator<<(ostream& out, const Date& d)
{
out << d.year_ << "年" << d.month_ << "月" << d.day_ << "日" << endl;
return out;
}
friend istream& operator>>(istream& in, Date& d);
public:
Date(int year = 2024, int month = 1, int day = 1);
Date(const Date& x);
void Print();
static int GetMonthDay(int year, int month);
Date& operator=(const Date& x);
int operator-(const Date& d)const;
bool operator<(const Date& x2)const;
bool operator==(const Date& x2)const;
bool operator<=(const Date& x2)const;
bool operator>=(const Date& x2)const;
bool operator!=(const Date& x2)const;
bool operator>(const Date& x2)const;
Date operator-(int x)const;
Date& operator-=(int x);
Date operator+(int x)const;
Date& operator+=(int x);
Date operator++(int);
Date& operator++();
Date& operator--();
Date operator--(int);
void Print() const;
private:
int year_;
int month_;
int day_;
};
Date::Date(int year, int month, int day)
{
if (month > 0 && month < 13 && day>0 && day <= GetMonthDay(year, month))
{
year_ = year;
month_ = month;
day_ = day;
}
else
{
cout << "非法日期" << endl;
}
}
Date::Date(const Date& x)
{
//cout << "拷贝构造" << endl;
year_ = x.year_;
month_ = x.month_;
day_ = x.day_;
}
Date& Date::operator=(const Date& x)
{
//cout << "赋值重载" << endl;
year_ = x.year_;
month_ = x.month_;
day_ = x.day_;
return *this;
}
bool Date::operator<(const Date& x2)const
{
if (this->year_ < x2.year_)
{
return true;
}
else if (this->year_ == x2.year_ && this->month_ < x2.month_)
{
return true;
}
else if (this->year_ == x2.year_ && this->month_ == x2.month_ && this->day_ < x2.day_)
{
return true;
}
return false;
}
void Date::Print()
{
std::cout << year_ << "-" << month_ << "-" << day_ << std::endl;
}
bool Date::operator>(const Date& x2)const
{
return !(*this <= x2);
}
bool Date::operator==(const Date& x2)const
{
return year_ == x2.year_ && month_ == x2.month_ && day_ == x2.day_;
}
bool Date::operator<=(const Date& x2)const
{
return *this < x2 || *this == x2;
}
bool Date::operator>=(const Date& x2)const
{
return !(*this < x2);
}
bool Date::operator!=(const Date& x2)const
{
return !(*this == x2);
}
Date Date::operator+(int x)const
{
if (x < 0)
{
return *this - -x;
}
Date d = *this;//调用拷贝构造
d.day_ += x;
while (d.day_ > GetMonthDay(d.year_, d.month_))
{
int monthday = GetMonthDay(d.year_, d.month_);
d.day_ -= monthday;
d.month_++;
if (d.month_ == 13)
{
d.year_++;
d.month_ = 1;
}
}
return d;
//Date d = *this;//调用拷贝构造
//d += x;
//return d;
}
Date& Date::operator+=(int x)
{
/*day_ += x;
while (day_ > GetMonthDay(year_, month_))
{
int monthday = GetMonthDay(year_, month_);
day_ -= monthday;
month_++;
if (month_ == 13)
{
year_++;
month_ = 1;
}
}
return *this;*/
* this = *this + x;
return *this;
}
int Date::GetMonthDay(int year, int month)
{
//四年一闰,百年不闰,四百年一闰
static int daysArr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//频繁调用
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))//优先判断是否是2月
{
return 29;
}
return daysArr[month];
}
//前置++
Date& Date::operator++()
{
return *this += 1;
}
//后置++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date Date::operator-(int x)const
{
if (x < 0)
{
return *this + -x;
}
Date d = *this;
d.day_ -= x;
while (d.day_ <= 0)
{
d.month_--;
if (d.month_ == 0)
{
d.year_--;
d.month_ = 12;
}
int monthday = GetMonthDay(d.year_, d.month_);
d.day_ += monthday;
}
return d;
}
Date& Date::operator-=(int x)
{
*this = *this - x;
return *this;
}
//前置--
Date& Date::operator--()
{
return *this -= 1;
}
//后置--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
int Date::operator-(const Date& d)const
{
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)
{
int year, month, day;
in >> year >> month >> day;
if (month > 0 && month < 13 && day>0 && day <= Date::GetMonthDay(year, month))
{
d.year_ = year;
d.month_ = month;
d.day_ = day;
}
else
{
cout << "非法日期" << endl;
}
return in;
}
void Date::Print() const
{
cout << "Print()const" << endl;
cout << "year:" << year_ << endl;
cout << "month:" << month_<< endl;
cout << "day:" << day_ << endl << endl;
}
3.4.5cout打印自定义类型
1.可以支持内置类型是因为,库里面对流插入进行了运算符重载,实现了函数重载;
2.可以支持自动识别内置类型是因为函数重载;
3.用友元突破封装;
4.写get函数;
3.4.6拷贝构造和赋值重载的区别
拷贝构造是用一个已经初始化的对象去初始化另一个未初始化的对象,而赋值重载则是两个已初始化的对向,一个给另一个赋值。
class Date
{
public:
Date(int year = 2024, int month = 1, int day = 1);
Date::Date(const Date& x)
{
cout << "拷贝构造" << endl;
year_ = x.year_;
month_ = x.month_;
day_ = x.day_;
}
Date& Date::operator=(const Date& x)
{
cout << "赋值重载" << endl;
year_ = x.year_;
month_ = x.month_;
day_ = x.day_;
return *this;
}
private:
int year_;
int month_;
int day_;
};
int main()
{
Date d1(1,1,1);
Date d2 = d1;//编译器会做处理,将其识别成拷贝构造;
Date d3(1, 1, 1);
d3 = d1;//赋值重载
return 0;
}
3.5取地址运算符重载
3.5.1const成员
对*this进行const修饰
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
1.要修改的对象不能加const;
2.只读对象都应该加const,因为这样可以使得普通对象和const对象都可以调用;
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << "Print()" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
void Print() const
{
cout << "Print()const" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
void Test()
{
Date d1(2022,1,13);
d1.Print();
const Date d2(2022,1,13);
d2.Print();//这里不能调用,权限会放大
}
3.5.2取地址运算符重载的实现
一般不需要自己实现
class Date
{
public :
Date* operator&()
{
return this ;
return nullptr;//不想让别人拿到地址
}
const Date* operator&()const//构成函数重载
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};