目录
初始化列表
注意
单/多参数传入
explicit关键字
匿名函数
先前,我们知道有构造函数——用于给各成员变量一个初始值。
但是仍然不能称为是初始化,因为初始化只能初始化一次,但是构造函数里可以多次赋值
初始化列表
以日期类Date为例,进行一个初始化列表
class Date
{
public:
//初始化列表—每个成员定义的地方
Date(int year, int month, int day)
:_year(year) //成员变量会被赋值为括号内相应的值
, _month(month)
, _day(day)
,_x(1)
{
//_day=day;//此处只能写在中括号里,中括号外必须要有' () '去定义
} //中括号内实现其他代码
private:
int _year;
int _month;
int _day;
const int _x=10;//const修饰的变量必须要在定义的时候初始化-后续不能修改,总之在类里必须有个地方进行赋值初始化
};
注意
1.缺省值是赋予初始化列表的
2.若初始化列表没有显示给值,就用这个缺省值
3.如果显示给值了,就不用这个缺省值(会先赋值缺省值,如果在列表里重新定义,就会重新赋值)
4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关
单/多参数传入
单参数直接用 ' () '或者用' = '进行拷贝构造,多参数不仅可用' () ' 还能用' {} '
//单参数
class A
{
public:
A(int i)
:_a(i)
{
cout << "a(int i)" << endl;
}
private:
int _a;
};
//多参数
class B
{
public:
B(int b1, int b2)
//explicit B(int b1, int b2)
:_b1(b1)
, _b2(b2)
{
cout << "B(int b1, int b2)" << endl;
}
private:
int _b1;
int _b2;
};
int main()
{
const A& ref = 2;
B bb1 (1, 2);
B bb2 = { 2,2 };
const B& ref2 = { 3,3 };//相同空间用引用,需转换类型,加const修饰
return 0;
}
explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。
class A
{
public:
//explicit A(int i)//在构造函数这里加上explicit,可以防止隐式转换——这样主函数的两个转换就不能使用了
A(int i)
:_a(i)
{
cout << "A(int i)" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
~A()
{
cout << "Delete" << endl;
}
private:
int _a;
};
int main()
{
A aa1(1);
/单参数构造函数的隐式类型转换
//用2调用A构造函数,先生成一个临时对象,再用这个对象去拷贝构造aa2
//编译器会优化,优化用2直接构造
A aa2 = 2;//结果是直接构造,而不是先对2进行了拷贝构造,再把这个对象赋值给aa2
//2会进行隐式类型转换,过程中会生成临时对象,具有常性,所以需要const去修饰
const A& ref = 2;//2转换,生成了一个临时对象具有常性,需要用const来修饰
return 0;
}
用explicit修饰构造函数,将会禁止构造函数的隐式转换。
匿名函数
顾名思义,就没进行命名的对象——生命周期仅存在于定义的那一行
A aa6(6);//有名——生命周期在当前局部域
A (7);//匿名——生命周期只在这一行
//运行后发现的确调用了一次析构,说明出了这一行就会销毁
//先定义再传入数据,代码量多
SeqList s;
s.PushBack(aa6);
//用匿名对象传,直接放入想要的数据
s.PushBack(A(8));
除此之外,匿名函数还有其他用法
class Solution {
public:
int Sum_Solution(int n) {
// ...
cout << n << endl;
return n;
}
private:
};
int main()
{
Solution s1;
s1.Sum_Solution(10);
Solution().Sum_Solution(100);//这种方法无需定义一个对象,直接赋值,调用类里的函数。
return 0;
}
Static成员
现在我们知道,一个类里边可以建立很多个对象,那我们是否可以统计出累积创建了多少个对象,并且执行到某一处正在使用的还有多少个对象。
引入
可以用以下代码
//全局变量
int n=0;
int m=0;
class A
{
A()
{
++n;
++m;
}
A(const A& t)
{
++n;
++m;
}
~A()
{
--m;
}
void Print()
{
cout<<m<<" "<<n<<endl;
}
private:
}
int main()
{
A aa1;
A aa2;
cout << n << " " << m << endl;//共有情况,非private
A();
cout << n << " " << m << endl;
Func(aa1);
cout << n << " " << m << endl;
return 0;
}
结果:
修正
但是如果将n和m放在全局变量的话,容易被修改,那怎么解决呢?——放入类里,仅需要改变private里的内容
class A
{
public:
{
//....
}
//private:
//静态成员变量属于所有A对象,属于所有类
/*int n=0 ;//创建
int m=0 ;//正在使用 */err,因为这样的话,每个对象都会有一个m和n,但我们仅需要一个全局的,所有对象共有的
static int n;
static int m;
}
int A::m=0;
int A::n=0;
访问形式
int main()
{
A aa1;
A aa2;
cout << A::n << " " <<A:: m << endl;//共有情况,非private
cout << aa1.n << " " << aa1.m << endl;
return 0;
}
static定义的特点
1.static定义的变量处于静态区里,不参与类大小的计算
sizeof(A);//———— 1
因为不参与类大小的计算,所以内存可视为0,但是类的构建是需要内存的,所以最少会分配一个字节的空间
int main()
{
A* ptr = nullptr;
cout << ptr->n << " " << ptr->m << endl;//为什么行得通?
return 0;
}
因为此处n和m存在于静态区,类似于成员函数的公共代码区,所以一样可以访问。
2.静态成员变量属于所有A对象,属于所有类
3.在类中由于用static定义了——所以属于全局,所以不能给缺省值
4.静态变量不能走初始化列表,不属于对象里,故不能给缺省值,是在静态区里的
5.限制了,不能在外边直接访问并且修改,除非得到m/n的别名,访问方式同其他类' :: '
' . '
static静态成员函数
因为n和m是属于所有A对象,属于所有类,所以需要调用一次类去打印
若用匿名函数
int main()
{
A();//能否用匿名函数去打印?
A();
A::Print();
return 0;
}
若用匿名函数,那么同样也会调用一次类,那么就会对数据造成误差。
此时就要用到静态成员函数
static void Print()
{
//x++; //不能访问非静态,因为没有this指针
cout << m <<" "<< n << endl;
}
静态成员函数特点
1.不具有this指针
2.不能访问非静态变量(仅能访问static定义的变量)
倘若想对m和n进行修改呢???——就需要静态引用返回——既不会额外改变m也不会直接改变m
static int& GetM()
{
return m;
}
++A::GetM();
例题
现在我们知道了通过每次的构造函数,可以实行计数
(求1+2+3+...+n_牛客题霸_牛客网)
我们需要建立两个类,一个类用于返回结果,另一个用来统计——怎么统计?——通过每次的构造实现n个有序数字的累加
class Sum
{
public:
Sum()//构造函数,开创一次就进入一次
{
_ret+=_i;
_i++;
}
static int GetRet()
{
return _ret;
}
private:
static int _ret;//用static修饰,这样可以保证所有对象共用这个_ret
static int _i;
};
int Sum::_ret=0;
int Sum::_i=1;
class Solution
{
public:
int Sum_solution(int n)
{
Sum a[n]
return Sum::GetRet();
}
};
友元函数
对于有private限定符的类,若是某个定义在类外的函数需要调用类内部的数据,那么就需要友元函数
以输出流/输入流为例
class Date
{
//友元声明——这样可以在外部访问私密对象
//friend void operator<<(ostream& out, const Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
{
//..
}
private:
{}
}
内部类
特点/定义
1.一个类定义在另一个类的内部
2.内部类是一个独立的类,不受限制,且是外部类的友元
class C
{
public:
class D
{
public:
void FuncD()
{
;
}
private:
int _d;
};
private:
int _c;
};
sizeof(C);// ——4
读取类C大小为4——因为内部类D还是独立的,所以读取的还是C的大小。
3.内部类是外部类的友元,内部类可以读取外部类的变量
class C
{
public:
class D
{
public:
void FuncD()
{
C cc;
cc._c = 1;//内部类D可以直接访问外部类C的私有成员变量
}
private:
int _d;
};
void func()
{
D dd;
}
private:
int _c;
};
4.public不能去掉,这样的话D类就不属于C类的公共区域里,
int main()
{
C::D dd1;//但是定义还是得用C类域内部的D类定义
//若去掉public,那么D类不是公共的,就不能调用
cc.func();//也不能调用,因为func是属于D类的,但是D类不公共
return 0;
}
内部类实现循环
再次谈回上边的例题,这次可以通过内部类来实现循环
(求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com))
class Solution {
class Sum
{
public:
Sum()
{
_ret+=_i;
_i++;
}
};
public:
int Sum_Solution(int n) {
Sum a[n];
return _ret;
}
private:
static int _ret;
static int _i;
};
int Solution::_ret=0;
int Solution::_i=1;
同理,还是需要用到static静态成员,将每次的_ret和_i 进行保存在全局变量里
本质上还是用到了构造函数的访问进行累加