构造函数
首先讨论下什么是构造函数*?
它是创建对象时,给成员变量赋初始值的一个函数。
通常情况下我们会这样为变量赋初值:
//假设一个日期类
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
但这里并不是初始化,因为初始化只能初始化一次,而在类体中可以多次为变量赋值。
初始化列表
实现: :成员变量1(初始值:形参/表达式)
,成员变量2(初始值:形参/表达式)
...
注意:
- 只有构造函数才有成员变量初始化列表。在初始化期间会为各变量划分空间。
- 初始化顺序是根据声明成员变量声明的顺序进行的,与其在初始化列表的顺序无关。
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
可以在初始化列表进行初始化也可以在构造函数内进行初始化,那怎么选择?
类类型成员(该类有非缺省的构造函数),引用类型 和 const类型的变量初始化一定要放在初始化列表中初始化,因为它们都要求在定义
时就要初始化。
在有下面这种情况:Date类中创建Time类成员变量时,无法为其赋初始值,这就使得Time类的构造函数为全缺省的构造函数 或者 Date类在其初始化列表的位置显示调用并赋初始值给它。
类型转换
构造函数除了能够创建对象和初始化对象外,还可以对单参的构造函数进行类型转换。
这段代码执行时会报错,在这里"d = 1997"会进行隐式转换。
因为单参构造函数,全缺省构造函数都可以被调用所以系统不知道调用那个,就会报错。
解决上述问题的方式,就是在单参构造函数声明前加"explicit"
关键字: explicit
作用:禁止单参的构造函数类型隐式转化。
静态成员
在c++类中声明成员时可以加上static关键字,这样声明的成员就叫做静态成员。
特性:
- 静态成员变量必须在类外进行显式初始化,定义时不添加static关键字;
class Date{
public:
Date(int year = 2018, int month = 10, int day = 12)
:_month(month)
,_day(day)
{}
private:
static int _year; // 声明 "_year" 为静态变量
int _month;
int _day;
};
int Date::_year = 2018; // 类外初始化静态变量
- 静态成员为所有类对象所共享,即使没有对象创建,类的静态数据成员也存在;
- 从外部访问类的静态成员通过"类名 :: 静态成员",“对象 . 静态成员” 或者 指针 来访问;
int main()
{
cout << Date::PrintYear() << endl; // 类名直接访问
Date d1;
Date* d2;
int tmp = d1.PrintYear(); // 对象.引用的方式
int tmp2 = d2->PrintYear(); // 指针方式
return 0;
}
- 静态成员函数没有隐藏的this指针,不能直接访问任何非静态成员,但是可以通过对象名间接的访问;
class Date{
public:
Date(int year = 2018, int month = 10, int day = 12)
:_month(month)
,_day(day)
{}
static void Print(const Date &d) // 通过引用已创建好的对象来访问其他非静态成员变量
{
int year = _year;
//int month = _month; // 由于静态函数没有this指针,
//int day = _day; // 就无法直接访问非静态成员变量
int month = d._month;
int day = d._day;
cout << year << "-" << month << "-" << day << endl;
}
private:
static int _year;
int _month;
int _day;
};
int Date::_year = 2018;
int main()
{
Date d1;
Date::Print(d1);
return 0;
}
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值,const修饰符等参数;
- 由于静态成员是在静态存储区,而不是堆栈区创建的,所以静态成员变量在对象中不占内存。
友元
友元函数 & 友元类
优缺点:友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数
假如我们重载输出操作符"<<",如果放在类内当作一个成员函数,由于类的成员函数第一个参数一定是隐含的this指针,所以我们调用方式一定是使用Date类的对象(this指针),然而这样的输出语句不符合我们习惯常用的。为此我们就需要在类外定义一个具有全局属性的operator函数,但这样又没办法使用类中的私有成员变量,此时就可以借助友元函数的方式解决。
用法: 友元函数可以在类内任何位置声明(别在定义成员变量那就行),并且要在声明前加 “friend”,它的定义放在类外与普通函数方式一样。
这样友元函数就可以直接访问类的私有成员变量。
总结:
- 友元函数可访问类的私有成员,但不是类的成员函数; // 子类也就不能继承基类的友元函数
- 友元函数不能用const修饰 ; // 因为声明中const修饰的是实际是this指针,而友元函数不属于类函数,所以没有this指针,也就不能被const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制;
- 一个函数可以是多个类的友元函数;
- 友元函数的调用与普通函数的调用原理相同;
- 友元关系是单向的,不具有交换性,不能传递;