一、构造函数补充
1.构造函数体的赋值
我们来看下面代码:
class Goat
{
public:
int a1; //定义
int a2;
const int x;
};
int main()
{
Goat aa;// 这里是对对象整体的定义,但是每个成员什么时候初始化呢?
return 0;
}
这里就会报错
因为const修饰的变量只能在定义的时候初始化
必须要给每个成员变量找个单独定义的位置,不然遇到const这样的成员不好处理。
所以C++创始人给这样的情况创建了初始化列表
1.2 初始化列表
class B
{
public:
B(int b)//需要传参不是默认构造函数
:b(0)
{
cout << "B()" <<endl;
}
private:
int b;
}
class Goat
{
初始化列表在默认构造函数下:由“:”开头,“,”分割
1.那个对象调用构造函数,初始化列表就是它所有成员变量定义的位置
2.不管是否显示写在初始化列表,每个变量都会在初始化列表定义
A()
:x(1)
,a1(1)
,
{
a1++;
a2--;
}
public:
int a1;
int a2;
const int x;
};
int main()
{
Goat aa;// 这里是对对象整体的定义,但是每个成员什么时候初始化呢?
B bb;
return 0;
}
C++规定有三种情况必须在初始化列表初识化
- const int x。(const修饰的变量)
- int& ref (引用时必须在初识化列表初始化)
- 对于自定义类型成员。(没有默认构造的自定义类型成员)
注意: 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
1.3隐式类型转化
class A
{
public:
A(int)
:a1(1)
{}
private:
int a2;
int a1;
};
int main()
{
A aa1(1); //调用构造函数
A aa2 = 1; //类型转换 (隐式类型转换)
int i = 1;
double d = i;//隐式类型转化
}
为什么[ A aa2 = 1 ] 时隐式转化呢?
我们来先看下面double类型的转换
所以自定义类型A也是同理
所以这里[ A aa2 = 1]应该经历一个构造函数和一个拷贝构造的过程。
不过在最近新的编译器中,这种连着一起执行的构造+拷贝构造,编译器会识别并且直接优化成一个构造。(一定要是构造+拷贝连着一起的执行才会优化,如果分开执行那就不会优化)
1.4explicit关键字
构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用(隐式类型转换)。接收单个参
数的构造函数具体表现:
class A
{
public:
// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
explicit A(int a)
:_a1(a)
{
cout << " A(int a)" << endl;
}
//2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用
explicit A(int a1,int a2)
:_a1(a1)
,a2(a2)
{}
private:
int _a2;
int _a1;
}
int main()
{ //单参数构造函数 C++98就支持了
A aa1(1); //调用构造函数
A aa2 = 1; //类型转换 (隐式类型转换)
//多参数隐式类型转换 C++11之后支持
A aa3(1,1);
A aa4 = {2,2};//也是先构造再拷贝构造不过被编译器优化成了构造
//注意这里赋值的话要用花括号。
}
二、 static成员
2.1 概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
面试题:实现一个类,计算程序中创建出了多少个类对象。
静态成员变量必须赋初始值
class A
{
public:
A()
{++_count;}
A(const A& t)
{++_count;}
int GetCount()
{
return _count;
}
private:
不属于某个对象,属于所有对象,属于整个类
static int _count;
}:
int main()
{
A aa1;
A aa2(aa1);
1.用这种方式i想要获得count的值,执行后我们会发现找不到,因为count是私有变量
cout << A::count << endl;
2.解决方法在class里public写上GetCount()函数,因为GetCount是类里面的函数对象,所以能直接访问到私有变量。
cout << aa1.GetCount() <<endl;
}
但是以上方法会有局限性,如果main函数这样写呢
void func()
{
A aa1;
A aa2(aa1);
}
int main()
{ func()
//这样就会访问不到
cout << aa1.GetCount() <<endl;
//只能为了获取到count的值再创建一个对象
A aa;
cout << aa.GetCount()-1 <<endl;
}
2.2静态成员函数
1.要想有很好的方式解决以上问题,我们需要用到静态成员函数。
//静态成员函数--没有this指针
static int GetCount()
{
return _count;
}
//调用方法:直接类类型调用
cout << A::GetCount() <<endl;
静态成员函数一般匹配这静态成员变量使用。
2.补充数组构造函数
这里我们的count值是多少呢?
答案是12,因为数值里每个值都是A对象类型,所以要调用10次构造函数。
三、友元类
前面已经看过了友元函数今天来看一下友元类
友元类要注意的是:
1.友元关系是单向的,不具有交换性。
2.友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。
3.友元不能继承
class Time
{
friend class 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 = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
四、内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中 的所有成员。但是外部类不是内部类的友元。
特性:
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。
class A
{
private:
static int k;
int h;
public:
//内部类 -- 跟A是独立的,只是受A类域的限制,B类的空间是独立的,不会占A类的空间大小。
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b; //B类的用法和定义
b.foo(A());
return 0;
}