初始化列表
简介
a.初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个**放在括号中(规定死了)**的初始值或表达式。
b.初始化列表可以认为就是对象成员变量定义的地方(有些成员必须在定义的地方初始化)
1.
例如 const int _n;
int& _ref;
只能在定义的时候初始化;
2.自定义类型成员也必须在初始化列表里初始化;
打样
Data (int year =1 ,int month =1 ,int day =1)
:_year(year)
,_month(month)
{
,_day(day)
}
注意点
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
Data()
:_year = 2
,_year = year-------➡️大错特错,成员变量在初始化列表只能初始化一次
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
自定义类型成员(该类没有默认构造函数)
1.
class A
{
A(int a)--------故意不写成默认构造函数
{}
private:
int _a;
};
2.
A就算是有默认构造函数但是若要在Data里初始化A的成员变量
需要这样!
a.
Data (int year =1, int month =1, int day =1,int a)
{
A aa(a);
_aa = aa;
_aa(a)-----❌:此会说调用operator()函数,所以必须先构造和_aa相同的类型,然后对其进行赋值!
}
b.
Data (int year =1, int month =1, int day =1,int a)
:_aa(a)--------➡️所以他会调用_aa的构造函数 ,效率会比上面那种高一些!
{......}
class Data
{
public:
Data (int year =1, int month =1, int day =1,int a)
:_aa(a)----------所以必须在初始化列表初始化!
,_year(year)
{.....}
private:
A _aa;
};
这里的Stack初始化也是在初始化列表里面初始化的。
例题
✳️成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
此处会先对_a2进行初始化。
特殊的隐式类型转换
包含explict 关键字
class Data
{
pubilic:
explicit Data(int year)--------➡️若没加“explicit”则支持Data d1=2022;否则就不会支持隐式类型转换
:_year(year)
{}
};
int main()
{
Data d1(2022);-----➡️就只有“构造”
Data d2 = 2022;-----➡️按理来说是“构造(先用2022构造一个Data对象)”,然后再用这个对象去“拷贝构造”d2;
若编译器大胆会优化成合二为一就只有“构造”;
为以后string做铺垫:string s1 = “hello”;
const Data& d5 = 2022;-------➡️若不加const 则引用过不去;
Func(const string& s)--------➡️所以必须要有const!
{}
Func("hello);
return 0;
}
static成员(静态成员)
概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态的成员变量一定要在类外进行初始化
面试题
实现一个类,计算程序中创建了多少个对象
int count = 0;--------➡️不推荐使用全局变量
class A
{
public:
A()
{
count++;
}
A(const A& aa)
{
count++;
}
static int GetCount()-------➡️静态成员函数,没有this指针!
{
_a=0---------❌:因为没有this指针,无法访问非静态成员!
return _count;
}
private:
int _a;
static int _count;-------➡️静态成员变量属于整个类,属于所有的对象
static int _count=0-------❌:因为静态他不在初始化列表定义,而是在类外
};
int A::_count = 0;--------➡️静态成员必须在类外面定义!
int main()
{
cout << A:: _count << endl;-------➡️通过类域去访问,但成员变量也得放成公有的,
否则就写GetCount()函数,但是若没有对象调用的话,
则要将其定义为静态成员函数
cout<<A::GetCount()<<endl;------➡️无对象
cout<<a.GetCount()<<endll----➡️有对象
return 0;
}
特性
- 静态成员为所有类对象所共享,不属于某个具体的实例
- 静态成员变量必须在类外定义,定义时不添加static关键字
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
有意思的代码题
由于不让我循环,则我们调用n此构造函数,所以创建n个对象(可以用数组,也可以new n个对象);
友元
友元函数
问题:现在我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这
样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。operator>>同理。
friend std::ostream& operator <<(std::ostream& out,Data& d);//friend声明的函数不属于类域,此处是全局函数。
若在类里面实现重载流插入/提取则用法会比较不太好。
说明:
1.友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
2.友元函数可访问类的私有和保护成员,但不是类的成员函数
3.友元函数不能用const修饰-----➡️因为const修饰的是“this”指针,但友元函数没有用到“this”指针
4.友元函数可以在类定义的任何地方声明,不受类访问限定符限制
5.一个函数可以是多个类的友元函数
6.友元函数的调用与普通函数的调用和原理相同
友元类
打样
class Date; // 前置声明
class Time
{
friend class Date;-------➡️声明友元类
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
int _year;
int _month;
int _day;
Time _t;
};
简介
1.友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
2.友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
3.友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元
内部类(C++很少用)
打样
1.
// 内部类
class A
{
private:
static int k;
int h;
//public:
// 内部类
class B --------➡️B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
private:
int _b;
};
✳️验证A不是B的友元
void Print(const B& b)
{
b._b = 0;------------❌:无法调用B的成员变量
};
2.若要计算A的大小:sizeof(A)
因为创建对象A并没有创建对象B出来,所以首先B的大小不在其内
其次,静态成员变量属于整个类,不在某一个对象空间里面,所以其也不算进大小里面