一、构造函数(初始化列表)
我们都知道构造函数的作用:使用类创建对象时,对对象进行初始化,但是我们所理解的初始化和真正的初始化是有所不同的
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;
};
3、值得注意的知识点:
①初始化列表中每个成员变量只能初始化1次
②以下类的成员变量必须在初始化列表中初始化:
(1)引用类型的成员变量
(2)const修饰的成员变量
(3)自定义类型的成员变量且该类没有默认构造函数
③尽量使用初始化列表进行初始化(即使没有初始化列表,对于自定义类型而言,也会先使用初始化列表初始化)
④成员变量在类中的声明次序,就是初始化的顺序(与初始化列表中的顺序无关)
4、explicit关键字(与强制类型转换有关):
我们一定都见过这样的代码语句:
double x = 5.5;
int a = x; //实际上会将x强制类型转换成int类型的值存放在一个临时变量中,然后赋值给a
内置类型能强转我们是知道的,那自定义类型呢?
答:自定义类型也可以强转
我们可以这样写:
calss A
{
public:
//explicit A(int a = 15) ----- 用explicit关键字修饰构造函数后可以避免强制类型转换
A(int a = 15)
{
_a = a;
}
private:
int _a;
};
int main()
{
A aa(12);
}
上述是比较常见的书写方式,那这样呢?
calss A
{
public:
A(int a = 15)
{
_a = a;
}
private:
int _a;
};
int main()
{
A aa;
aa = 20; //我们也可以用强转来解释这一行代码,但是如果想避免强转可以使用explicit关
//键字
}
二、static成员
类中声明用static修饰的成员变量为静态成员变量,用static修饰的成员函数称之为静态成员函数。
1、使用方法:
①静态成员为所有类对象共享,不属于某个具体的对象,存放在静态区
②静态成员变量必须在类外定义,定义时不可加static(由①静态成员不属于对象推断得来)
③类静态成员用: 类::成员 或 对象.成员 的方式访问
④静态成员函数没有隐藏的this指针,因此也不能访问任何非静态成员
⑤静态成员也是类的成员,也要受到public、private、protected的限制
2、计算创建了几次类对象:
class A
{
public:
A()
{
++_count;
}
A(const A& a)
{
++_count;
}
~A()
{
--_count;
}
static int Getcount()
{
return _count;
}
private:
static int _count;
};
int A::_count = 0;
void TestA()
{
cout << A::Getcount()<<endl;
A a1,a2;
A a3(a1);
cout << A::Getcount()<<endl;
}
答案是:3次
三、友元
1、友元函数:
相信我们大家都知道输出输入流(<< 、>>)吧。那么,我们不妨大胆一点,如果我们把这两个操作符重载,要怎么操作?怎么使用?
首先我们来看一下正常使用:
class A
{
public:
A(int a = 5)
{
_a = a;
}
private:
int _a;
};
int main()
{
A a1;
cout<<a1._a<<endl;
}
①我们来进行重载:
class A
{
public:
A(int a = 5)
{
_a = a;
}
ostream& operator<<(ostream& _cout)
{
//打印对象中的成员变量值
_cout<<_a<<endl;
Return _cout;
}
private:
int _a;
}
int main()
{
A a1;
//使用输出流操作符
//因为我们在使用操作符调用成员函数时会传递一个隐含参数this指针,这个
//指针指向的是调用此函数的对象,所以这种写法和我们平时使用的有点不同
a1<<cout;
}
上面核心点在于:调用成员函数需要使用到隐含参数才可以访问到对象成员,所以格式会有限制
②那么问题又来了:如何才能像我们平时一样去使用呢?
友元函数登场!
class A
{
public:
A(int a = 5)
{
_a = a;
}
//使用友元函数前面加个 friend
friend ostream& operator<<(ostream& _cout , const A& a)
private:
int _a;
}
//友元函数
ostream& operator<<(ostream& _cout , const A& a)
{
//打印对象中的成员变量值
_cout<<a._a<<endl;
return _cout;
}
int main()
{
A a1;
//打印
cout<<a1.a;
}
上面核心点在于:
①首先友元函数使用就是在前面加上一个friend,非常简单
②友元函数不属于类,所以友元函数也不是成员函数,需要传递相关的参数(包括调用函数的对象,因为不是成员函数没有this指针)
③同一个友元函数可以被多个类调用
④友元函数也不能用const修饰(因为友元函数不能被限定,所有的类都可以调用)
2、友元类:
既然存在友元函数,那么友元类的存在也就合情合理了。何为友元类?我们可以来类比一下友元函数:如果一个函数是友元函数,那么类中加上关键字friend后,这个函数在类中就可以访问类的私有成员变量!(就好比这个函数表明自己是其他类的朋友,那朋友就是用来索取的,所以这个函数就可以肆无忌惮的以朋友“friend”的名义肆无忌惮的使用类中的成员变量)。所以,友元类也是同理,一个类是另一个类的友元类,那么这个类只要愿意,是可以肆无忌惮的使用另一个类的成员函数以及被保护的成员变量。
但是有一点需要注意,你是我的友元类,你可以肆无忌惮的调用我的资源,但是你的资源我无权操纵,因为朋友关系只是我单方面建立的,你并没有建立。(我说你是我的朋友,你可以随意从我这里拿东西,但是你没有承认我是你的朋友,那我别想拿你的东西!)
所以,总结友元类:
①一个类若是另一个类的友元类,那么这个类就可以打着“friend”的名义去调用另一个类。
②友元类的关系是单向的(我说you are my friend,你可没说)
③友元类的关系不可传递(你是我朋友,我是他朋友,你跟他并不一定是朋友)
④友元类的关系不可继承
代码:
class Time
{
friend class Date;
public:
time(int hour, int minute, int second)
:_hour(hour)
,_minute(minute)
,_second(second)
{}
private:
int _hour;
int _minute;
int _second;
}
class Date
{
public:
Date(int year,int month, int day)
:_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;
}
Date类是Time类的友元类 -> “ Date 是 Time 的朋友” -> Date类 可以调用Time类中的等等。
四、内部类
1、概念:如果一个类在另一个类的内部创建,那么这个类就是另一个类的内部类,另一个类就是外部类。但实际上,除了内部类是外部类的友元类之外:①两个类并没有包含与被包含的关系 ②外部类对内部类也没有优越于其他独立类的操作权限。
2、特性:
①内部类可以定义在外部类中的public、protected、private
②外部类的大小就是整个类的大小,并不包含内部类