默认成员函数
接下来继续看剩下的两个默认成员函数。
const成员函数
将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后 ⾯。const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。
先来看例子:
所以C++设计了使用 const 修饰成员函数来达到目的。
所以,只需在成员函数参数列表后添加 const ,就能解决问题。
所以,对于一个类,如果我们希望所有的成员函数都默认为const,即不修改对象状态,我们可以使用const关键字来修饰成员函数。
取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和 const 取地址运算符重载,⼀般这两个函数编译器⾃动 ⽣成的就可以够我们⽤了,不需要去显⽰实现。取地址的意思就是返回当前对象的地址,对于成员函数来讲,this指针就是它的地址。
class Date
{
public://普通取地址运算符重载Date* operator&(){return this;}//const取地址运算符重载,返回一个const Date* 的指针。const Date* operator&() const{return this;}private:int _year;int _month;int _day;
};
友元
友元就是提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类 声明的前⾯加 friend,并且把友元声明放到⼀个类的⾥⾯。外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。
#include<iostream>class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//友元函数声明friend void func(const Date& d);private:int _year;int _month;int _day;
};void func(const Date& d)
{std::cout << d._year << "年" << d._month << "月" << d._day << "日" << std::endl;
}int main()
{Date d1;func(d1);return 0;
}
并且⼀个函数可以是多个类的友元函数。
#include<iostream>//前置声明,否则Date类中的友元函数不认识A类
class A;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//友元函数声明friend void func(const Date& d, const A& a);private:int _year;int _month;int _day;
};class A
{friend void func(const Date& d, const A& a);private:int _a1 = 1;int _a2 = 2;
};void func(const Date& d, const A& a)
{std::cout << d._year << "年" << d._month << "月" << d._day << "日" << std::endl;std::cout << a._a1 << std::endl;
}int main()
{Date d1;A a;func(d1, a);return 0;
}
友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
#include<iostream>class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}friend class A;private:int _year;int _month;int _day;
};class A
{
public:void func(const Date& d){std::cout << d._year << "-" << d._month << "-" << d._day << std::endl;std::cout << _a1 << ' ' << _a2 << std::endl;}
private:int _a1 = 1;int _a2 = 2;
};int main()
{Date d;A a;a.func(d);return 0;
}
友元类的关系是单向的,不具有交换性。比如A类是B类的友元,但是B类不是A类的友元。而且友元类关系是不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
赋值重载流插入和流提取
上一篇的利用运算符重载在类中实现了各种操作(详见:点这里),接下来就在类内实现流插入和流提取的重载(也就是在类内实现自定义类型的输入输出)。
流插入
通过查阅资料可以看到,cin 是 istream 类型的对象,cout 是 ostream 类型的对象,如果要重载,则类型简单可以写为:
但这样写是有问题的,是不对的。
其报错信息显示出,出现这样的错误本质上是因为参数不匹配造成的。
如果改为这样,就不会报错,也能正常运行。
所以,重载 >> 和 << 时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 << cout 对象,不符合使⽤习惯和可读性。重载为全局函数把 ostream/istream 放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。
但又出现了新的问题,就是无法访问类内的成员变量。所以我们可以使用C++的 友元 来访问类内部的成员变量。
但其还不支持连续赋值,和=等运算符一样,它一定是要支持连续赋值的,所以还要进行改造。
但与=运算符不同,流插入流提取运算符是从左到右赋值的。所以得:
#include<iostream>
using namespace std;class Date
{
public:// 全缺省的构造函数Date(int year = 1900, 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;}// 赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}// 析构函数//日期类无要释放的资源,编译器自动生成的析构即可满足要求。void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}//友元函数声明friend ostream& operator<<(ostream& out, const Date& d);private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}void test()
{Date d1(2024, 6, 6);Date d2(2006, 1, 2);cout << d1 << d2;
}int main()
{test();return 0;
}
接下来就是流提取得重载函数。
流提取
流提取是 istream 类型的对象,所以:
#include<iostream>
using namespace std;class Date
{
public:// 全缺省的构造函数Date(int year = 1900, 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;}// 赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}// 析构函数//日期类无要释放的资源,编译器自动生成的析构即可满足要求。void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);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)
{cout << "请输入年月日:";in >> d._year >> d._month >> d._day;return in;
}void test()
{Date d1(2024, 6, 6);Date d2(2006, 1, 2);cin >> d1 >> d2;cout << d1 << d2;
}int main()
{test();return 0;
}
内部类
如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独立的类,跟定义在 全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。并且内部类默认是外部类的友元类。
简单来说就是,内部类可以访问到外部类成员,但外部类不能访问到内部类得成员。
#include<iostream>class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}class A{public:void func(const Date& d){std::cout << d._year << "-" << d._month << "-" << d._day << std::endl;std::cout << _a1 << ' ' << _a2 << std::endl;}private:int _a1 = 1;int _a2 = 2;};private:int _year;int _month;int _day;
};int main()
{Date d1;Date::A a;a.func(d1);return 0;
}
匿名函数
直接⽤ 类型 (实参) 定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象。匿名对象⽣命周期只在当前⼀⾏,⼀般用于临时定义⼀个对象。
#include<iostream>class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}private:int _year;int _month;int _day;
};int main()
{//有名函数Date d1;d1.Print();//匿名函数Date().Print();return 0;
}