一、初始化列表
1、认识
【P】Stack
不具备默认构造,MyQueue
也无法生成默认构造
【S】引入初始化列表
MyQueue(int n)
:_pushst(n)
,_popst(n)
,_size(0)
{}
初始化列表本质上可以理解为每个对象中成员定义的地方
所有成员既可以在初始化列表初始化,也可以在函数体初始化,下面的只能在初始化列表处初始化:
- 引用
const
- 自定义类型成员没有默认构造(必须显式传参调构造)
const
变量必须在初始化列表中定义,因为const
变量只有一次初始化的机会,必须在定义时初始化
2、注意事项
- 初始化列表无论写不写,每个成员变量都会先走一遍
- 自定义成员会调用默认构造
- 内置类型有的缺省值的用缺省值,没有的话看编译器,有的编译器会处理
先走初始化列表,再走函数体
【实践中】尽可能用初始化列表初始化,不方便再用函数体初始化
public:
MyQueue()
:_size(1) //显式写了就不用缺省值了
{}
private:
int _size = 0; //缺省值(给初始化列表使用)
【注】声明处的值是缺省值,并非定义,而是提供给初始化列表时使用的
3、初始化顺序
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
class A {
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private: // 初始化顺序与声明次序相同
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
return 0;
}
输出结果:
1 -858993460
二、单/多参数构造函数与隐式类型转换
在类中,声明成员变量给缺省值时也可以利用到【单/多参数构造函数与隐式类型转换】
B bb1(10);
// 拷贝构造
B bb2 = bb1;
// 隐式类型转换
// 内置类型转为自定义类型(单参数构造函数支持)
B bb3 = 1;// 1构造一个B的临时对象,再用这个临时对象拷贝构造bb3
B bb4 = 3.14;
B bb5 = 'a';
//B bb6 = "abcd"; // 字符串不可以
// 临时变量具有常性
const B& bb6 = 3.14;// bb6引用的是用3.14构造的临时对象
// 编译器遇到连续构造和拷贝构造->优化为直接构造
// 应用场景
// 原本
Stack st1;
B bbo(1);
st1.Push(bbo);
cout << endl;
// 简化后
st1.Push(2);
// 又例
list<string> lt;
string s1("abc");
lt.push_back(s1);
// 简化后
lt.push_back("abc");
C cc1(10, 20);
C cc2 = { 100,200 };
//C cc2 = (100,200);// 错误写法,但如果有单参数构造函数可能不会报错,逗号表达式只出一个结果
const C& cc3 = { 1000,2000 };
Stack st2;
st2.Push(cc1);
// 简化后
st2.Push({10,20});
三、静态成员变量
// 静态计数器
class MyClass {
public:
MyClass() {
++_instanceCount;
}
MyClass(const MyClass& other) {
++_instanceCount;
}
~MyClass() {
--_instanceCount;
}
// 没有this指针,只能访问静态成员
static int getInstanceCount() {
return _instanceCount;
}
private:
int _i1 = 1;
int _i2 = 1;
static int _instanceCount;
};
// 定义
int MyClass::_instanceCount = 0;
MyClass mctest() {
MyClass mc4;
return mc4;
}
int main(){
MyClass mc1;
cout << "sizeof(mc1) = " << sizeof(mc1) << endl;
cout << "_instanceCount = " << MyClass::getInstanceCount() << endl;
MyClass mc2(mc1);
MyClass mc3 = mc2;
mctest();
cout << "_instanceCount = " << MyClass::getInstanceCount() << endl;
MyClass obj = mctest();
cout << "_instanceCount = " << MyClass::getInstanceCount() << endl;
return 0;
}
结果为
sizeof(mc1) = 8
_instanceCount = 1
_instanceCount = 3
_instanceCount = 4
【练习】
解答:
#include <iostream>
using namespace std;
class Sum {
public:
Sum() {
_ret += _i;
++_i;
}
static int GetRet() {
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
int main() {
Sum arr[100];
cout << "1至100依次的加和是:" << Sum::GetRet() << endl;
return 0;
}
四、友元
(1)友元函数
具体用例详见专栏里【240414 类和对象】文章中的【二、日期类:5. 流插入、流提取】部分
(2)友元类
单向,不具有交换性,不能传递,不能继承
class Time {
//声明Date类是Time类的友元
friend class Date;
public:
//...
private:
int _h;
int _m;
int _s;
//...
};
class Date {
public:
Date(int h, int m, int s) {
_t._h = h;
_t._m = m;
_t._s = s;
}
//...
private:
Time _t;
//...
};
五、内部类
内部类就是外部类的友元类
B天生是A的友元
class A {
public:
class B {
public:
B() {
}
void funcB() {
}
private:
int _b1;
int _b2;
};
A() {
}
void funcA() {
}
private:
int _a;
};
A aa;
cout << "sizeof(A) = " << sizeof(aa) << endl;
A::B bb;
cout << "sizeof(B) = " << sizeof(bb) << endl;
结果:
sizeof(A) = 4
sizeof(B) = 8
B是与A平行的独立类,仅仅只受类域限制
将第三部分的【练习】题改写成内部类
class Solution {
private:
class Sum {
public:
Sum() {
_ret += _i;
++_i;
}
static int getRet() {
return _ret;
}
private:
static int _i;
static int _ret;
};
public:
int sum_solution(int n) {
Sum* arr = new Sum[n];
int result = Sum::getRet();
delete[] arr;
return result;
}
};
int Solution::Sum::_i = 1;
int Solution::Sum::_ret = 0;
main 函数:
Solution sol;
cout<< "1至100依次的加和是:" << sol.sum_solution(100) << endl;