1.初始化列表
构造函数中,为一个成员赋值,只能说对此成员赋初始值,不能称之为初始化,初始化列表才是真正初始化成员变量的地方
1.1初始化列表的使用
例:
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{
_year++;
_day--;
_month = month;
}
private:
int _year;
int _month;
int _day;
};
有些变量在初始化时必须对它赋值
-
const成员变量
-
引用成员变量
-
没有默认构造的自定义类型成员
然而在构造函数的函数体中的赋值行为不是对变量的初始化,在使用上述类型时会报错,所以必须用初始化列表
class B
{
public:
B(int a, int ref)
:_n(10)
,_ref(ref)
,_obj(a)
{}
private:
const int _n; // const修饰成员
int& _ref; // 引用成员
A _aobj; // 没有默认构造函数的自定义类型
};
对于自定义成员来说,不管有没有显示写初始化列表,它都会优先使用初始化列表初始化,所以在之后尽量使用初始化列表进行初始化
1.2初始化列表的细节
初始化列表中,初始化变量的顺序是变量在类中声明的顺序
class A
{
public:
A(int i)
:_a1(i)
,_a2(_a1)
{}
private:
int _a2;
int _a1;
};
int main()
{
A temp(3);
}
此时,_a2会先初始化,_a1再初始化
因为_a2初始化时_a1还是随机值,所以_a2就被初始化成了随机值,而_a1会被初始化为3
class A
{
public:
A(int i)//没有显示传参,就用缺省值初始化
:_a1(i)
,_a2(i)
{}
private:
int _a2 = 1;
int _a1 = 2;
};
int main()
{
A temp();
}
当用户没有显示传参进行初始化时,编译器会用成员变量的缺省值初始化
当用户显示传参时,使用用户传的值初始化
2.static成员
- 声明为static的类成员称为类的静态成员
- 用static修饰的成员变量称之为静态成员变量
- 用static修饰的成员函数称之为静态成员函数
静态成员变量一定要在类外进行初始化
static成员的特性:
- 静态成员为所有类对象共享,放在静态区
- 静态成员变量必须在类外定义,类中只是声明
- 类静态成员可用
类名::静态成员
或对象.静态成员
访问 - 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员受访问限定符的限制
class B
{
public:
static int Add(int x,int y);//没有this指针,无法访问类中成员
static int a;//在类中声明
};
int B::a = 10;//在类外定义
3.友元
3.1友元函数
一个函数定义在类外,但又想访问类中的私有成员,只能将这个私有成员改成公有后再访问,但这种操作就破坏了类的封装
引入友元:
友元函数可以直接访问类的私有成员,它是定义在类外部的函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字
class Date
{
friend int Add(int x,int y);//友元函数的声明
public:
Date(int year = 1999, int month = 9, int day = 9)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
int Add(int x,int y)//友元函数的定义
{
x += _year;
y += _month - _day;
return x + y;
}
友元函数特点:
- 友元函数可访问类的私有成员和保护成员,但自身不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数相同
3.2友元类
除了有友元函数可以访问类私有成员外,声明友元类也可以达到一样的效果,而内部类就是友元类的典型
内部类是一个定义在另一个类内部的类,内部类是一个独立的类,不属于外部类,不能通过外部类的对象访问内部类的成员(外部类对内部类,没有任何优越的访问权限)
内部类是外部类的友元:内部类可以通过外部类的对象参数访问外部类的所有成员
外部类不是内部类的友元
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//无报错
cout << a.h << endl;//无报错
}
};
};
4.类的匿名对象
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d = Date(2023,8,1);//Date(2023,8,1)就是匿名对象
return 0;
}
特点:
- 生命周期只有一行
- 只有类名,没有变量名,在初始化或销毁时自动调用构造或析构函数
当定义第一个变量只是为了调用类中的一个函数,并且调用完后此变量不会再被使用,此时可用匿名对象
5.explicit
int main()
{
A a(1);//构造函数初始化对象a
A b = 1;//对象d没有被初始化,直接被赋值,产生隐式类型转换:int类型转换为A类型
return 0;
}
内置类型int对于自定义类型A的类型转换,通过构造函数产生一个类型为A的临时变量,再通过拷贝构造把临时变量传给b
但是程序运行发现,只进行了两次构造,并没有拷贝构造,说明C++对于自定义类型产生临时变量,编译器会做优化
#include<iostream>
using namespace std;
v
class A
{
public:
explicit A(int n)//构造
:_a(n)
{
cout << "A(int n)" << endl;
}
A(A& d)//拷贝构造
{
cout << "A(A&d)" << endl;
}
private:
int _a;
};
int main()
{
A a(1);//构造函数
A b = 1;//隐式类型转换 加入关键字explicit会报错
return 0;
}
为了防止隐式类型转换发生,所以在构造函数前加入关键字explicit