行业知识
【C++】初始化列表、static成员、友元、匿名对象、附练习题
2026-10-21 12:57:52
来源:互联网转载
文章目录
- 前言
- 一、构造函数【初始化列表】
- 1.1 构造函数体赋值
- 1.2 初始化列表
- 1.3 explicit关键字
- 二、static成员
- 2.1 概念
- 2.2 特性
- 三、友元
- 3.1 友元函数
- 3.2 内部类
- 四、匿名对象
- 4.1 拷贝对象时的一些编译器优化
- 五、再次理解类和对象
- 六、练习题
- 6.1 求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句
- 6.2 计算日期到天数的转换
- 6.3 日期差值
- 6.4 打印日期
- 6.5 ���加天数
前言
我们前面学习了关于类和对象的6个默认成员函数,知道了一个空类中有构造函数和析构函数,通过对对象初始化和对象中进行资源清理,达到初始化和销毁的作用。我们再对一些小的点进行补充,看看类和对象的一些细节。
一、构造函数【初始化列表】
1.1 构造函数体赋值
- 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
class Date{public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;};- 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
1.2 初始化列表
- 初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
- 初始化列表是每一个成员定义的地方
class Date{public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day;};【注意】
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 自定义类型成员(且该类没有默认构造函数时)
class A{public:A(int a):_a(a){}private:int _a;};class B{public:B(int a, int ref):_aobj(a) // 进行显示调用, _ref(ref), _n(10){}private:A _aobj; // 没有默认构造函数int& _ref; // 引用const int _n; // const};- const和引用必须走初始化列表
- 自定义类型,如果没有默认构造函数就会报错,我们需要进行显示调用【()里面是参数】
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
class Time{public:Time(int hour = 0):_hour(hour){cout public:Date(int day){}private:int _day;Time _t;};int main(){Date d(1);}public:A(int a):_a1(a), _a2(_a1){}void Print() {cout A aa(1);aa.Print();} public:// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译explicit Date(int year):_year(year){}/*// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}*/Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day;};void Test(){Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值d1 = 2023;// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转换的作用}public:A() { ++_scount; }A(const A& t) {++_scount; }~A() {--_scount; }static int GetACount() { return _scount; }private:static int _scount;};// 放在外面进行初始化int A::_scount = 0;void main(){cout public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 _cout friend ostream& operator}private:int _year;int _month;int _day;};ostream& operator_cout _cin d._year;_cin d._month;_cin d._day;return _cin;}int main(){Date d;cin d;cout private:static int k;int h;public:class B // B天生就是A的友元{public:void foo(const A& a){cout A::B b;b.foo(A());return 0;}public:A(int a = 0):_a(a){cout cout public:int Sum_Solution(int n) {//...return n;}};int main(){A aa1;// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义//A aa1();// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数A();A aa2(2);// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说Solution().Sum_Solution(10);return 0;}public:A(int a = 0):_a(a){cout cout cout _a = aa._a;}return *this;}~A(){cout }A f2(){A aa;return aa;}int main(){// 传值传参A aa1;f1(aa1);cout class Sum { public: Sum() { _ret += _i; _i++; } static int GetRet() { return _ret; } }; public: // sum是Solution的友元 int Sum_Solution(int n) { Sum a[n]; return _ret; } private: static int _i; static int _ret;};int Solution::_i = 1;int Solution::_ret = 0; int year, mon, day; int data[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; while (cin year mon day) { int sum = 0; for (int i = 1; i sum += data[i]; } if ((mon == 2) && ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0))) { // 如果是闰年就要多加1天 sum = sum + 1 + day; } else { // 需要加上最后的天数 sum = sum +day; } cout 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};//给出年月日,计算距离0000年0月1日的天数和int CountDay(int y, int m, int d){ // 计算0-y年的天数 int yearDay = y * 365 + y / 4 - y / 100 + y / 400; // 计算到0-m月的天数 int monthDay = mon[m - 1]; if (m 2 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)) monthDay += 1; return yearDay + monthDay + d;}int main() { int year1, month1, day1; scanf("%4d%2d%2d", &year1, &month1, &day1); int n1 = CountDay(year1, month1, day1); int year2, month2, day2; scanf("%4d%2d%2d", &year2, &month2, &day2); int n2 = CountDay(year2, month2, day2); cout int year; int day; int mon[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; while (cin year day) { if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)) mon[2] = 29; else mon[2] = 28; for (int i = 1; i if (day printf("%04d-%02d-%02d\n", year, i, day); break; } else { day -= mon[i]; } } }} int m = 0; while (cin m) { int year, mon, day, num; for (int i = 0; i
- 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
- 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。