在C++初阶(4)中,我们具体实现了Date日期类。今天我想继续往下讲C++的其他一些语法知识点。
一、构造函数的初始化方式
使用构造函数对对象进行初始化时,有两种方式:
(1)函数体内实现
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
(2)使用初始化列表初始化
class Date
{
public:
Date(int year, int month, int day)
:_year(year),
_month(month),
_day(day)
{}
private:
int _year;
int _month;
int _day;
};
但注意,有三种成员变量必须使用初始化列表初始化。
初始化列表是成员变量的定义阶段,而const和引用类型的变量必须在定义后立马初始化,所以必须放在初始化列表初始化
3.没有默认构造函数的自定义类型变量
举例说明:
class A
{
//没有默认构造函数
};
class Date
{
public:
Date(int year, int month, int day)
:_n(10)
, _ref(year)
, _a(1)//因此A的对象_a要放在初始化列表中初始化
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
const int n;
int& ref;
A _a;
};
另外,成员变量在初始化列表中的初始化顺序由类中的声明次序决定,与其在初始化列表中的先后顺序无关
因此建议:在类中声明的成员变量的顺序与初始化列表中的顺序保持一致
举例说明:
//下面程序的输出结果是? D
class A
{
private:
int _a2;
int _a1;
public:
//先初始化_a2,为随机值 再初始化_a1,为1
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print()
{
cout << _a1 << "" << _a2 << endl;
}
};
int main()
{
A.aa(1);
aa.Print();
return 0;
}
A.1 1 B.程序崩溃 C.编译不通过 D.1 随机值
二、Static
·static成员变量
class A
{
private:
static int n;//n存在于静态区,属于整个类也属于类的所有对象
//静态成员不能在构造函数里初始化
};
int A::_n = 0;//在类外单独初始化,前面不加static
//注意不能在main函数中初始化
static变量存在于静态区,属于整个类,不属于某个单独的对象。
因此静态成员不能在构造函数里初始化,要在类外单独初始化,且前面不加static
由于静态成员属于整个类,因此既可以通过类名访问,也可以通过类的某个对象访问
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year = 2021, int month = 8, int day = 15)
{
_year = year;
_month = month;
_day = day;
}
static int n;
};
int Date::n = 2021;
int main()
{
Date d1(2021,8,19);
Date d2(d1);
Date d3(d2);
cout << Date::n << d1.n << d2.n << d3.n << endl;
return 0;
}
·静态成员函数
静态成员函数的特点:
另外,静态成员函数不能调用非静态成员函数,非静态成员函数能调用静态成员函数
举例说明:
class A
{
private:
int a;
static int n;
public:
static int Get_n()
{
a = 1;//error!不能访问a
return n;
}
//所以 如果某函数专门是为了访问静态成员,可以设计成静态函数
};
下面,我们通过一个例题,来更好地了解静态成员函数的作用
例题1:
求1+2+3+……10,不能使用乘除法,for while if wlse switch case
class Add
{
public:
Add()
{
sum += n;
++n;
}
Add(const Add& a)
{
++n;
}
void Print()
{
cout << Add::sum << endl;
}
private:
static int n;
static int sum;
};
int Add::n = 1;
int Add::sum = 0;
int main()
{
Add aa[10];//创建对象数组,相当于调用10次构造函数
aa[0].Print();
return 0;
}
三、友元
·友元函数
声明形式:
friend 返回类型 函数名(参数列表)
特点:
我们通过重载<<和>>来举例说明:
重载<<时,要实现连续输出,必须是cout<<变量的形式。如果设计成普通成员函数,第一个参数默认为this指针,调用时只能写成变量>>cout*,就没法连续输出了
另外,函数的返回类型应是ostream类的引用
重载>>同理。
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year = 0, int month = 0, int day = 0)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
};
ostream& operator<<(ostream& out, const Date& d)//注意这里d也要取引用
{
//这里设计成友元而不设计成成员函数。
//因为成员函数的第一个参数必定是隐藏的指针,这样就没法把cout写在左边了
//为了实现cout<
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)//注意这里d也要取引用
{
in >> d._year >> d._month >> d._day;
return in;
}
int main()
{
Date d1;
Date d2(2021, 5, 25);
cin >> d1 >> d2;
cout << d1 << d2;
return 0;
}
·友元类
特点:
举例说明:
class Time
{
//声明Date为Time类的友元类,可以在Date类中访问Time类的私有成员
friend class Date;
private:
int _hour;
int _minute;
int _second;
public:
Time(int hour = 0,int minute=0,int second=0)
:_hour(hour)
,_minute(minute)
,_second(second)
{}
};
class Date
{
private:
int _year;
int _month;
int _day;
Time _t;
public:
Date(int year=2021,int month=0,int day=0)
: _year(year)
,_month(month)
,_day(day)
{
_t._hour = 1;
}
void SetTimOfDate(int hour, int minute, int second)
{
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
};
但是要说明的是:友元是一种破坏封装的行为。因此一般情况下,不建议使用友元 。