再谈构造函数
初始化列表:成员(初始化值/表达式),成员2()…
每一个初始化变量只能在初始化列表中出现一次。
对象的成员变量定义的地方:初始化列表。
如果有引用成员,const成员,没有默认构造函数的自定义成员,必须在初始化列表中进行初始化。
其他成员可以不进行显示初始化。
class Time
{
public:
Time(int a)
{
_a = a;
}
private:
int _a;
};
class Date
{
public:
Date(int y = 1, int m = 1, int d = 1)
:_a(d)
,_b(d)
,_time(10)
{
_year = y;
_month = m;
_day = d;
}
private://成员变量声明的地方,此处不是定义的地方
int _year;
int _month;
int _day;
int& _a;//引用在定义时必须初始化
const int _b;//const变量在定义时必须初始化
Time _time;//没有默认构造的自定义成员在定义时必须初始化
};
对于自定义成员,最好在初始化列表中初始化,即使不再初始化列表中显示初始化,编译器也会在初始化列表中自动调用自定义成员的默认构造完成初始化。
成员变量在初始化列表中的初始化顺序:和声明顺序一致,与其在初始化列表中的顺序无关。
注意:最好初始化列表中的成员顺序和声明顺序保持一致。
class A
{
public:
// explicit: //可以禁止掉单参构造函数的隐式类型转化
A(int a)
:_a(a)
{}
A(const A& a)
:_a(a._a)
{}
private:
int _a;
};
void test()
{
A a(1);//创建对象
A a2 = 2;//创建对象:单参构造函数的隐式类型转换 ---> 调用构造创建一个匿名对象,+拷贝构造(通过匿名对象拷贝构造a2对象)
//此时用2创建了一个匿名对象,在调用拷贝构造创建a2
a2 = 3;//3作为参数传入构造函数, 创建匿名对象,调用赋值重载函数,使用匿名对象给a2赋值
//只支持单参构造函数的隐式类型转换,下面的代码不支持
//a3 = (1, 2);
}
创建对象:单参构造函数的隐式类型转化—>调用构造创建一个匿名对象,+拷贝构造(通过匿名对象拷贝构造a2对象)。
只支持单参构造函数的隐式类型转换,下面代码不支持。
explicit:禁止单参构造的隐式类型转换。
static 成员
静态成员:static+成员变量/函数
静态成员函数:函数内部没有this指针
static可以修饰类的成员变量,成为静态成员变量,也可以修饰成员函数,称为静态成员函数。
静态的成员变量,必须要在类外进行初始化_,定义时不加static关键字。
class A
{
public:
A()
{_count++;}
A(const A& t)
{_count++;}
static int GetCount()
{return _count;}
private:
static int _count;
};
int A::_count = 0; //静态的成员变量,必须要在类外进行初始化
void test()
{
cout << A::GetCount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetCount() << endl;
}
特点:
- 静态成员变量,所有对象共享此成员变量,可以看成类成员。使用时类名::静态成员变量或对象
- 静态成员遍历不能在声明的时候给默认。
- 静态成员不在对象模型中,一般存放在数据段,不能再初始化列表中初始化。
- 静态成员必须在类外初始化。
普通成员只能通过对象访问,不能通过类名访问。
静态成员变量/静态成员函数访问方式:
- 对象访问
- 类名+作用域限定符
静态成员函数不能访问非静态成员函数/变量—>因为非静态成员函数需要this指针,但是静态成员函数内部缺少this指针,所以不能访问。
非静态成员函数可以访问静态成员函数/变量。
友元函数
友元函数:friend+正常函数的定义/声明。
友元函数可以访问类的私有成员。
友元函数不是类的成员函数,它是普通的非成员函数。(没有this指针)
只需要在类中声明友元函数,不需要在类中定义。
返回输出流是为了支持连续赋值。
友元函数尽量少用,它是一种突破封装的函数。
友元函数可以声明在类的任何地方,不受访问限定符的限制。
class A
{
public:
friend ostream& operator<<(ostream& _cout, A& a);//返回输出流类型是为了支持连续输出
A()
{_count++;}
A(const A& t)
{_count++;}
static int GetCount()
{return _count;}
private:
static int _count;
};
int A::_count = 0; //静态的成员变量,必须要在类外进行初始化
ostream& operator<<(ostream& _cout, A& a)
{_cout << a._count << endl;}
void test()
{
cout << A::GetCount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetCount() << endl;
cout << a1 << a2 << a3;
}
display,fun,funl都为Date类的友元函数。
友元类
友元类:friend class 类名,相当于普通朋友
class B
{
public:
//DisPlay, Fun1, Fun2都为A类的友元函数
void DisPlay(const A& a)
{
cout << a._a << endl;
}
void Fun1(const A& a)
{
cout << a._a << endl;
} void Fun2(const A& a)
{
cout << a._a << endl;
}
};
class A
{
public:
friend class B;
private:
int _a;
}
如果一个类是另一个类的友元类,则此类中的所有成员函数即为另一个类的友元函数。
友元关系是单向的,类似于单相思。友元关系不能传递,类似于朋友不一定是自己的朋友
内部类
内部类:在一个类内部定义一个新的类,相当于老铁/闺蜜。
class A
{
public:
class B
{
public:
private:
int _b;
class C
{
public:
private:
int _c;
}
}
private:
int _a;
}
内部类可以在类的任何地方定义。
内部类天然的作为外部类的友元类:
- 可以通过外部类对象访问外部类的私有成员
- 可以直接访问外部类的static成员)
内部类作为一个独立的类存在,不附属于外部类。
外部类不能看作内部类的友元类,对于内部类的成员没有特殊的访问权限,需要遵循访问限定符的限制。
总结
静态成员: static +成员变量/函数:
静态成员函数:
- 静态成员函数:函数内部没有this指针
- 静态成员函数不能访问非静态成员函数变量–>因为非静态成员需要this指针,但是静态成员函数内部缺少this指针,所以不能访问3.非静态成员函数可以访问静态成员函数变量
静态成员变量:
- 静态成员变量,所有对象共享此成员变量,可以看成类成员
- 静态成员变量不能在声明的时候给默认
- 静态成员不在对象模型中,一般存放在数据段, 不能在初始化列表中初始化
- 静态成员必须在类外初始化
静态成员变量/静态成员函数访问方式:
- 对象访问
- 类名+作用域限定符
- 普通成员只能通过对象访问,不能通过类名访问
友元函数: friend +函数定义:
- 友元函数尽量少用,它是一-种突破封装的语法
- 友元函数可以访问类的私有成员
- 友元函数不是类的成员函数,它是普通的非成员函数
- 只需要在类中声明友元函数,不需要在类中定义
- 友元函数可以声明在类的任何地方,不受访问限定符的限制
友元类: friend class 类名:
- 友元关系是单向的
- 如果一个类是另一个类的友元类,则此类中的所有成员函数即为另一个类的友元函数
- 友元关系不能传递,类似于朋友的朋友不一定是自己的朋友
内部类:在一个类内部定义一个新的类:
- 内部类天然的作为外部类的友元类
- 内部类作为一-个独立的类存在,不附属于外部类
- 外部类不能看作内部类的友元类,对于内部类的成员没有特殊的访问权限,需要遵循访问限定符的限制
- .可以通过外部类对象访问外部类的私有成员
- 可以直接访问外部类的static成员