再谈构造函数
初始化列表::成员(初始化/表达式),成员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);
}
explicit: 可以禁止掉单参构造函数的隐式类型转化
static 成员
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;
}
尽量少用,它是一种突破封装的语法
友元类
如果一个类是另外一个类的友元类,则此类中的所有成员函数即为另一个类的友元函数
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成员