我想让Date类这样的自定义类型,怎么样像内置类型一样,直接使用流提取、流插入打印呢?
Date d1(2022,10,12); cout<<d1; cout<<(d1+100);
我们不能往库函数里再加一个函数,我们尝试在Date类中重载
这是因为运算符有多个操作数的时候,第一个参数为左操作数,第二个参数是右操作数,那么很明显谁才是第一位:d1.operator<<(cout) d1
跑是能跑起来,但是这样合适吗?因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用
于是我们把它挪到类外,将operator<<重载成全局函数,摆脱隐藏的this指针约束,但是又有类外无法访问成员的问题
注:这里为了支持连续输出,重载函数需要有返回值ostream&,原理类似于连续赋值,只不过cout的结合性是从左至右:
cout<< d1<< d2
这时候就要引入友元来解决问题了
🌈友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制(但一般在开头)
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
#include<iostream> using namespace std; class Date { //友元函数 -- 这个函数内部可以使用Date对象访问私有保护成员 friend ostream& operator << (ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); public: Date(int year = 2022, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; ostream& operator << (ostream& out,const Date& d) { out << d._year << "-" << d._month << "-" << d._day << endl; return out; } istream& operator>>(istream& in, Date& d) { in >> d._year; in >> d._month; in >> d._day; return in; } int main() { Date d1(2022, 10, 26); Date d2(2022, 10, 12); cout << d1 << d2; cin >> d1 >> d2; return 0; }
ps:流提取的时候,需要写入,右操作数不能用const来修饰
🌈友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员(私有/保护)
友元关系是单向的,不具有交换性
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行
友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元
#include<iostream> using namespace std; class Date; // 前置声明 class Time { // 友元:声明Date为Time类的友元类,则在Date类中就直接访问Time类中的私有成员变量 friend class Date; public: Time(int hour = 0, int minute = 0, int second = 0) : _hour(hour) , _minute(minute) , _second(second) {} private: int _hour; int _minute; int _second; }; class Date { public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} void SetTimeOfDate(int hour, int minute, int second) { // 直接访问时间类私有的成员变量 _t._hour = hour; _t._minute = minute; _t._second = second; } private: int _year; int _month; int _day; Time _t; };
四、内部类
C++用的少,java用的多。谐音累不累,我累了
🌈如果一个类定义在另一个类的内部,这个类就叫做内部类。内部类天生就是就是外部类的友元类
内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元
特性:
内部类可以定义在外部类的public、protected、private都是可以的。
注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
class A { private: static int k; int h = 0; public: //B定义在A的里面 // 1、受A的类域的限制,访问限定符 // B天生是A的友元 class B { public: void foo(const A& a) { cout << k << endl; //可以访问外部类的static cout << a.h << endl;//可以访问外部类的private } private: int _b; }; }; int A::k = 1; int main() { A::B b; //只是受类域的限制 b.foo(A()); return 0; }
sizeof(外部类) = 外部类 ,和内部类没有任何关系
cout << sizeof(A) << endl;
五、练习(易错)
众所周知,编译器会优化代码 ——
先抛结论:连续一个表达式步骤中,连续构造一般会优化—— 合二为一
W f3() { W ret; return ret; } int main() { f3();//1次构造 1次拷贝 cout << endl << endl; W w1 = f3();//本来:1次构造 2次拷贝 ——》优化:1次构造 1次拷贝 }
那这里是怎么样优化的呢?
那么为什么可以跳过tmp,直接返回呢?这就要涉及到栈桢
问:以下代码调用了__次构造? __次拷贝构造?
W f(W u) { W v(u); W w = v; return w; } main() { W x; W y = f(f(x)); }
每层循环分开来看
这个图应该只有我能看懂了,所以优化完是7次的拷贝构造,不优化是:9次
六. 再次理解类和对象
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识
在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象
📢写在最后
dwg从网吧队打到世界冠军,我要是他,我比他还狂,冠军也会落寞,也印证了失败才是人生的主旋律,成功只是偶尔,加油少年