在本篇博客中,作者将会讲解类与对象的最后一篇。
一.再谈构造函数
在类与对象(上篇)中,我们讲到了构造函数,其实构造函数就是给每个成员变量进行赋值!!!
仅仅只是赋值而已,并不算严格意义上的初始化。
#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;
}