240416 初始化列表 构造与隐式类型转换 static成员 友元 内部类
一、初始化列表
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;