一,初始化列表
1,之前我们在实现构造函数的时候,初始化成员变量主要是在函数体内进行赋值,构造函数初始化还有另外一种方式,就是使用初始化列表。初始化列表的使用方式是:以一个冒号开始,接着以逗号分割的数据成员列表,每个成员变量后面跟一个放在括号里的初始值或表达式。
2,每个成员变量在初始化列表只能出现一次,语法上可以理解初始化列表是成员变量初始化定义的地方。内置类型可以不初始化,编译器就会给随机值。
class Date
{
public:
Date(int year=1,int month=1,int day=1)//初始化列表初始化
:_year(year)
,_month(month)
,_day(day)
{}
private:
//变量声明
int _year;
int _month;
int _day;
};
成员变量在初始化列表初始化后,还可以在函数体内用。
3,引用成员变量,const成员变量,没有默认构造函数的类类型成员变量,这三类是必须在初始化列表进行初始化的,不能在函数体内初始化。
(1)下面代码是这三类没有在初始化列表初始化。
class Time
{
public:
//默认构造函数有3类
//1,全缺省的
//2,无参的
//3,编译器默认生成的
Time(int a )//不是默认构造
:_a(a)
{}
private:
int _a;
};
class Date
{
public:
Date(int year=1,int month=1,int day=1)//初始化列表初始化
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
int& r;
const int i;
Time _t;
};
上面的这段代码就会报错
(2)原因
对于const修饰的变量,和引用成员变量。
对于没有默认构造的类类型成员变量 ,需要在初始化列表初始化。而有默认构造的,可以不用在初始化列表写,编译器会自动调用的默认构造进行初始化。
4,c++11后,还可以在成员变量声明的地方给缺省值
class Date
{
public:
Date(int year,int month,int day)//初始化列表初始化
:_year(year)
,_month(month)
//_day没初始化,会用缺省值初始化
{}
void print()
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
//声明,缺省值->在初始化列表没有初始化的,会用缺省值初始化
int _year=1;
int _month=1;
int _day=1;
};
int main()
{
Date d1(2024, 7, 15);
d1.print();
return 0;
}
5,成员变量在类中的声明顺序就是在初始列表中的初始化顺序。与其在初始化列表中的顺序无关。
6,总的来说,成员变量初始化尽量使用初始化列表。因为不管你是否使用初始化列表,对于自定义类型而言,一定会先使用初始化列表初始化。
二,内置类型转换
1,c++支持内置类型隐士转化成类类型对象,需要有相关内置类型为参数的构造函数
2,构造函数前加上explicit就不再支持隐士类型转换了。
class A
{
public:
//explicit A(int a=0) 加上explicit就不支持类型转换了
A(int a=0)
:_a(a)
{}
void print()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A a1(1);//调用构造函数
a1.print();
//类型转换
A a2 = 2;//先用2调用构造函数,再通过拷贝构造给a2
//但是编译器遇到连续构造+拷贝构造,会优化为直接构造
a2.print();
A& ra1 = a2;//引用
const A& ra2 = 2;//具有常性,直接引用会扩大权限,编译报错
return 0;
}
三,静态成员变量
1,用static修饰的成员,称为静态成员变量,静态成员变量一定要再类外进行初始化。
2,静态成员变量为所有类对象共享,不属于某个单独的类,存放于静态区。
3,用static修饰的成员函数成为静态成员函数,静态成员函数没有this指针。
4,静态成员函数可以访问静态成员变量,但不能访问非静态的,因为没有this指针。
5,非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
6,静态成员函数也受访问限定符private,public,protected的限制。
class A
{
public:
A()
{
_scount++;
}
A(const A& a)
{
_scount++;
}
~A()
{
_scount--;
}
static int GetScount()//静态成员函数,可以访问静态成员,没有this指针
{
return _scount;
}
private:
static int _scount;//静态成员变量
};
int A::_scount = 0;//类外初始化,需要指明类域
这样就可以计算一个程序中,对象被创建了多少个
int main()
{
A a1;//构造
A a2(a1);//拷贝构造
cout << A::GetScount() << endl;
//代码块的局部域
{
A a3;
cout << A::GetScount() << endl;
}
//出作用域后对象销毁,调用析构函数
cout << A::GetScount() << endl;
return 0;
}
四,友元类
1,友元分为友元函数和友元类,再函数声明或者类声明前加上friend,再放到类中。
2,外部友元函数可以访问类的私有和保护成员,友元函数仅仅是一种声明,不是成员函数。
3,友元函数可以放到类的任何地方,不受访问限定符的限制。
4,一个函数可以是多个类的友元函数。
5,友元类中的成员函数都可以看作是另一个类的友元函数,可以访问类中的私有和保护成员。
6,友元类的关系是单向的,并且不具有传递性。
总结:需要访问私有的时候,可以设置为友元。
友元函数
class Date
{
//声明为友元函数
friend ostream& operator<<(ostream& out, Date d);
public:
Date(int year, int month, int day)
: _year(year),
_month(month),
_day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, Date d)
{
//可以访问私有成员
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
友元类
class Time
{
friend class Date;
//声明Date类为友元类,则Date可以直接访问Time的私有成员变量
public:
Time(int hour = 0, int minute = 0,int second=0)
:_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 SetTime(int hour, int minute, int second)
{
//可以访问私有成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
五,内部类
1,如果一个类定义在另一个类内部时,这个类就叫做内部类。
2,内部类是一个独立的类,它不属于外部类,不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类时外部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。
六,匿名对象
class Date
{
public:
Date(int year=1, int month=1, int day=1)
: _year(year),
_month(month),
_day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
//Date d1();
//不能这么定义对象,因为编译器不知道是在定义函数,还是对象
Date();
//定义匿名对象,不用取名字
//声明周期只在这一行,到下一行它就会调用析构函数
return 0;
}