在构造函数中实现的给对象赋值,那不能叫做对对象初始化,只能被称为赋初值,因为赋值可以多次,而初始化只能初始化一次。
class Data
{
public:
//构造函数内部进行赋值操作,不能被称为初始化,因为可以进行多次赋值
Data(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
}
实际上,初始化是在初始化列表中实现的:
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
class Data
{
public:
//构造函数内部进行赋值操作,不能被称为初始化,因为可以进行多次赋值
Data(int year, int month, int day)
:_year(year)
,_month(1)
,_day(1+2)
{}
private:
int _year;
int _month;
int _day;
}
在对对象中的成员变量进行初始化时,每个成员变量只能出现在初始化列表中一次,
当类中包含 :引用成员变量 const成员变量和自定义类型成员变量时,必须在初始化列表中进行初始化
成员变量在类中的声明次序就是其在初始化列表中的初始化次序
而初始化规则为:
如果用户没有在初始化列表的位置显式给出初始化代码,则编译器会自动还原,内置类型,指针类型和自定义类型的变量被设置的方式不同:
如果成员变量是内置类型的变量:会被设置为随机值
如果成员变量是指针类型的变量:内置类型会被设置为0,然后将指针设置为nullptr
如果成员变量是自定义类型的变量:编译器会调用对应类中的无参构造方法或者全缺省的构造方法来初始化。
class Time
{
public:
Time(int hour, int minute, int second)
{
_hour = hour;
_minute = minute;
_second = second;
}
private:
int _hour;
int _minute:
int _second;
}
class Data
{
public:
Data(int year = 1900, int month = 1, int day = 1)
{
:_year(year)
,_month(month)
,_day(day)
//需要显式初始化_t1对象
,_t1(21, 20, 19)
}
private:
int _year;
int _month;
int _day;
//Data类中定义了Time的对象,
Time _t1;
}
int main()
{
Data d1(2023, 4, 8);
}
另外,成员变量在类中的声明次序就是其在初始化列表中的初始化顺序,和初始化列表中的先后次序无关
static是为了声明类中的成员,如果修饰的是类变量,那么就变成了静态成员变量,如果修饰的是类方法,那么就成了静态方法。特殊的是:静态成员变量需要在类外初始化。
静态成员具有一些特性:
首先:
被static修饰的方法是没有this指针的,由此可以得出
1.被static修饰的方法,不能被const修饰,(本质上是由于const修饰的是this指针,因此不能再被const修饰了)
2.类的静态成员是放在静态区的,并不是某个对象享有,而是这个类所有的对象都共享的。
3.类的静态成员比普通成员多了一种访问方式,普通成员只能通过 对象名.成员(可以是静态,也可以是非静态), 而静态成员可以通过类名::静态成员(只能是静态)
思考:静态成员函数可以调用非静态成员函数吗?非静态成员函数可以调用类的静态成员函数吗?
要想回答这个问题,我们就需要深入思考静态函数和非静态函数的区别,按照上述特性,静态函数没有this指针,是类共享的,放到静态区。
那么当通过静态成员函数调用非静态成员函数时,非静态成员函数的this指针就不知道是谁调用的它,从而编译报错。
再这样想,无论是静态成员函数还是静态成员变量,他们都是在类的范畴之外的,及在类的整个生存周期里始终只能存在一份。然而非静态成员变量和非静态成员函数是针对类的对象而言,通过静态函数调用非静态函数时,对象都没有创建,怎么能去调用非静态函数呢?(冠军还没到手,怎么能考虑冠军皮肤的事呢?)
那么第二个问题也很好回答了
用静态函数没有this指针,也就不需要知道是谁调用的它,自然可以被调用
友元可以分为友元函数和友元类,友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
在编写代码的时候,如果我们需要通过某个函数去访问类内的私有成员,或者对于类的参数传递顺序有特殊要求,那么就可以使用友元函数。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
由于类内函数默认第一个参数是this指针,那么使用输出运算符重载会导致顺序错乱,我们需要对函数参数的顺序进行调整,并且要访问类内私有成员,使用友元函数就可以解决。
要进行说明的是
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能被const修饰
可以在类内任意位置声明,但是需要在类外定义
和友元函数类似,如果我们要将一个类内的成员函数都设置为另一个类的友元函数,意味着这个类的成员函数都可以访问另一个类的非公有成员,(这样其实也破坏了类的封装性),这样逐个的去声明未免有些麻烦,就可以使用友元类,之间将当前类声明为另一个类的友元类,也同样实现了要求。
友元函数/类有其独有的性质:
1.友元关系是单向的,不具有交换性,当A类是B类的友元类,A类的成员可以访问B类的非公有成员,但是B类的成员不能访问A类的非公有成员。
2.友元关系不能传递,如果A类是B类的友元类,B类是C类的友元类,但是A类不是C类的友元类。
3.友元关系不能继承
在理解了友元类的基础上,实际上,A类中如果定义了另一个B类,我们就可以称B类为A类的内部类,这个内部的B类就是A类的友元类。那么这个B类也就拥有了和友元类相同的访问权限和性质。
内部类就是外部类的友元类,但是外部类不是内部类的友元类。
对于内部类,也有一些性质:
1.内部类可以定义在外部类的public、protected、private都是可以的。
内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
sizeof(外部类)=外部类,和内部类没有任何关系。