在本篇博客中,作者将会讲解类与对象的最后一篇。
一.再谈构造函数
在类与对象(上篇)中,我们讲到了构造函数,其实构造函数就是给每个成员变量进行赋值!!!
仅仅只是赋值而已,并不算严格意义上的初始化。
#include<iostream>
using namespace std;class Date
{
public://实例化对象时,同时给成员变量赋值Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Show(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Show();return 0;
}
初始化列表
既然普通构造函数并不是初始化,那么什么才是初始化呢?答案是初始化列表。
#include<iostream>
using namespace std;class Date
{
public://初始化列表用法Date(int year = 0, int month = 1, int day = 1):_year(year),_month(month),_day(day){}void Show(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Show();return 0;
}
为什么要有初始化列表呢?因为有些成员变量不能通过普通构造函数来赋值:如:引用变量、const常量、自定义类型(且该类没有默认构造函数)。
#include<iostream>
using namespace std;class Date
{
public://无默认构造函数(默认构造函数是指:不需要传参的的构造函数)Date(int year,int month,int day):_year(year),_month(month),_day(day){}void Show(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};class Temp
{
public:Temp(int& ptmp):_a(ptmp),_b(10),_d(0,0,0){}void Show(){cout << _a << " " << _b << endl;}
private://必须要用初始化列表的三个成员变量int& _a;const int _b;Date _d;//自定义类型,且无默认构造函数
};int main()
{int tmp = 0;Temp t1(tmp);return 0;
}
最后,初始化列表的初始化顺序与构造函数的初始化列表无关,与成员变量的定义先后有关
二.static成员
static成员变量
被static修饰的成员变量称为静态成员变量,静态成员变量不属于某个实例化对象,它是所有类对象共享的。且静态成员变量不能在类中初始化,必须在类外初始化。
#include<iostream>
using namespace std;class Date
{
private:int _year;static int count;
};int Date::count = 0;int main()
{return 0;
}
static成员函数
被static修饰的成员函数没有this指针。在前面讲到,类的成员函数都会带上一个默认的this指针,而被static修饰的成员函数没有this指针。
三.友元
友元函数
什么是友元函数?
我们知道在C++类中,成员变量都会有一个访问修饰符,其中private和protect是私有和保护的,被这两个修饰的成员变量不能被类外部访问,只能被类内部访问,那么如果我们想在外部用函数来对类内部的成员变量进行访问,就可以用友元函数。
#include<iostream>
using namespace std;class Date
{//声明Show是友元函数friend void Show(const Date& d);
public:Date(int year=0,int month=1,int day=1):_year(year),_month(month),_day(day){}
private:int _year;int _month;int _day;
};void Show(const Date& d)//可以在类外部访问类内部的成员变量
{cout << d._year << "-" << d._month << "-" << d._day << endl;
}int main()
{Date d1(2024, 3, 24);Show(d1);return 0;
}
看到这里,可能会有人疑惑,我直接在类内部定义一个Show函数就好了吗,为什么还要搞一个友元函数?因为有一些地方必须要用到友元函数。
友元函数重载<<
在C++中,如果我们想输出某个内置类型变量,直接cout<< 变量名即可,那如果我们想输出自定义类型,则需要额外写一个Show函数,而且每次都要调用该类的函数,那么有没有办法可以使用 cout<<类实例化对象 这样的方式来实现输出了,答案是可以的。
在类与对象(中篇)中,我们讲到了操作符重载,即我们可以重载<<这个操作符来实现。
常规做法实现
#include<iostream>
using namespace std;class Date
{
public:Date(int year=0,int month=1,int day=1):_year(year),_month(month),_day(day){}ostream& operator<<(ostream& out)//重载<<操作符{cout << _year << "-" << _month << "-" << _day << endl;return out;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 3, 24);Date d2(2024, 3, 25);d2 << (d1 << cout);return 0;
}
我们可以发现如果在类中定义重载<<操作符,会发现,它的使用方式和常规使用方式不同,它是反过来的,因为在operator<<函数中,会默认有个this指针占了第一个参数的位置,同时d1<<cout 等价于 d1.operator<<(&d1,cout),导致<<使用方法与原本相反,这时,我们就需要使用友元函数来解决。
友元函数实现
通过使用友元函数的实现可以达到我们想要的效果。
友元类
友元类就是,A类是B类的朋友,所以A类可以突破访问权限限制,可以去访问B类的非公有成员变量。
#include<iostream>
using namespace std;class Time
{friend class Date;//声明Date类是它的朋友
public:Time(int hour=0,int min=0,int second=0):_hour(hour),_min(min),_second(second){}
private:int _hour;int _min;int _second;
};class Date
{
public:Date(int year,int month,int day):_year(year),_month(month),_day(day){}void SetTime(int hour, int min, int second)//这个函数可以突破访问权限去访问Time的成员变量{_t._hour = hour;_t._min = min;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};int main()
{Date d1(2024, 3, 24);d1.SetTime(0, 0, 0);return 0;
}
但是这种关系是单向的,Date可以去访问Time,而Time不能去访问Date。同时友元关系不能传递,也不能继承。
四.匿名对象
匿名对象就是使用类来实例化一个匿名的对象,这个匿名的对象可以直接调用类种的成员函数,但是它的生命周期只有在定义它的那一行。
#include<iostream>
using namespace std;class Solution
{
public:int GetSum(int n){return n;}
};int main()
{Solution().GetSum(10);//实例化一个匿名类去知道调用它的成员函数return 0;
}