#include <iostream>
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void operator<<(ostream &out) // out是cout的别名
{
out << _year << '/' << _month << '/' << _day << endl;
}
void operator>>(istream &in)
{
in>>_year>>_month>>_day;
}
private:
int _year, _month, _day;
};
int main()
{
Date a(2023, 9, 16);
a>>cin;//a是被操作的对象,cin传给in
a<<cout;
return 0;
}
const的参与
cpp复制代码
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
printf("%d %d %d\n", _year, _month, _day);
}
private:
int _year, _month, _day;
};
int main()
{
Date a(2023, 9, 17);
const Date b(1,1,1);
a.Print();
b.Print();//这里会报错
/*
因为b是一个const Date类型的
b.Print()的原型是b.Print(&b),&b是一个const Date*类型的
void Print()的原型是void Print(Date* const this)
&b从const Date*变成Date*权限放大了
所以我们将void Print()写成void Print() const,这样他的原型变成了:
void Print(const Date* const this)是const Date*类型,后面那个const是修饰this
让this一直指向这个类且不能变
*/
return 0;
}
cpp复制代码
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()const
{
printf("%d %d %d\n", _year, _month, _day);
}
bool operator<(const Date& d)
{
return _year<d._year;
}
private:
int _year, _month, _day;
};
int main()
{
Date a(2023, 9, 17);
const Date b(1,1,1);
a<b;//不会报错
b<a;//这里会报错
/*
bool operator<(const Date& d)的原型是
bool operator<Date* const this,const Date& d)
所以写成b<a的时候(b是const Date*类型)
b由const Date*变成Date*,权限变大
可以写成
bool operator<(const Date& d)const
*/
return 0;
}
9.初始化列表
初始化列表可以认为是成员变量定义的地方
(private里面是声明)
9.1格式
cpp复制代码
#include <iostream>
using namespace std;
class Date
{
public:
//格式如下
Date(int a)
: _a(a),
_b(a)
{
}
void Print()
{
cout << _a << endl;
}
private:
int _a;
int &_b;
};
int main()
{
Date a(6);
a.Print();
return 0;
}
9.2例子
每个成员变量在初始化列表中只能出现一次
类中包含以下成员,必须放在初始化列表位置进行初始化
1.引用成员变量
2.const成员变量
3.自定义类型成员
cpp复制代码
class A
{
public:
A(int a)
: _a(a)
{
}
void Print()
{
cout << _a << endl;
}
private:
int _a;
};
class B
{
public:
B(int a, int b)
: _b(b),
_a(a),
_n(10),
_r(b)
{
}
void Print()
{
_a.Print();
cout << _b << endl;
}
private:
int _b;
A _a;
const int _n;
int& _r;
};
成员变量在类中声明的次序就是其在初始化列表里面初始化的次序,与其在初始话列表里面的先后顺序无关
例子:
cpp复制代码
#include <iostream>
using namespace std;
class A
{
public:
A(int a)
:_a(a),
_b(_a)
{}
void Print()
{
cout<<_a<<endl;
cout<<_b<<endl;
}
private:
int _b;
int _a;
};
int main()
{
A a(3);
a.Print();
return 0;
}
/*
输出结果是 3 随机值
因为先是把_a赋值给_b,再把a赋值给_a
*/
9.3explicit关键字
用explicit修饰的构造函数,会禁止单参构造函数的隐式转换
首先搞明白什么是隐式转换
cpp复制代码
class A
{
public:
A(int a)//单参构造函数
: _a(a)
{
}
private:
int _a;
};
//定义一个类对象
A a(10);
/*
此时_a的值为10,如果进行以下操作,a=20,这样子会把_a的值变为20
编译器会用20(将20赋值给无名对象的_a)构造一个无名对象
最后用无名对象赋值给对象a,这种转换就叫隐式转换
*/
隐式转换其实还有很多,比如
int a=2;
double b=a;
编译器会先引入一个中间变量,类型为double,然后再将这个值赋值给b
cpp复制代码
#include <iostream>
using namespace std;
class A
{
public:
A(int a)
/*
如果这里改成explicit A(int a)的话
a=5那行代码会报错
因为explicit会禁止单参构造函数的隐式转换
*/
: _a(a)
{
}
void Print()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A a(3);
a = 5;
a.Print();
return 0;
}
9.4 匿名对象
生命周期只有它所在的那一行
cpp复制代码
class A
{
public:
A(int a)
:_a=a
{}
private:
int _a;
};
A a(10);
a = A(20);
a.Print();
如果不使用匿名对象,那就需要:
A a(10);
A b(20);
a=b;
这样子来操作
/*
A(20)表示创建一个匿名对象,他的_a值为20
这个匿名对象创建的时候也会调用构造函数和析构函数
*/
10.友元类
如果类A是类B的友元类,那么B可以调用A里面的private成员和protected成员
但是这样子做会破坏类的封装性
注意点
友元关系不能被继承
友元关系是单向的,A是B的友元,但是B不一定是A的友元
友元不具有传递性,比如A是B的友元,B是C的友元,但是A不是C的友元
cpp复制代码
#include <iostream>
using namespace std;
class A
{
public:
friend class B;//代表A是B的友元类,B可以访问A里面的所有成员
A(int a)
{
_a=a;
}
private:
int _a;
};
class B
{
public:
B(int b)
{
_b=b;
}
void Show_A_a(A a)
{
cout<<a._a<<endl;//这里就体现了可以访问A定义的对象a里面的私有成员
}
private:
int _b;
};
int main()
{
A a(666);
B b(555);
b.Show_A_a(a);
return 0;
}