四川宜宾市网站建设公司,做招聘网站都需要什么手续,wordpress资讯站,北京网站建设模板案例第一#xff1a;我们不写时#xff0c;编译器默认生成的函数行为是什么#xff0c;是否满足我们的需求。第二#xff1a;编译器默认生成的函数不满足我们的需求#xff0c;我们需要自己实现#xff0c;那么如何自己实现#xff1f;二、构造函数构造函数是特殊的成员函数…第一我们不写时编译器默认生成的函数行为是什么是否满足我们的需求。第二编译器默认生成的函数不满足我们的需求我们需要自己实现那么如何自己实现二、构造函数构造函数是特殊的成员函数需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时空间就开好了)而是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能构造函数自动调用的特点就完美的替代的了Init。构造函数的特点函数名与类名相同。无返回值。 (返回值啥都不需要给也不需要写void不要纠结C规定如此)对象实例化时系统会自动调用对应的构造函数。构造函数可以重载。如果类中没有显式定义构造函数则C编译器会自动生成⼀个无参的默认构造函数一旦用户显式定义编译器将不再生成。无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数都叫做默认构造函数。但是这三个函数有且只有⼀个存在不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载但是调用时会存在歧义。要注意很多人会认为默认构造函数仅仅是编译器默认生成的那个。实际上无参构造函数、全缺省构造函数也是默认构造函数总结一下就是不传实参就可以调用的构造就叫默认构造。我们不写编译器默认生成的构造对内置类型成员变量的初始化没有要求也就是说是是否初始化是不确定的看编译器。对于自定义类型成员变量要求调用这个成员变量的默认构造函数初始化。如果这个成员变量没有默认构造函数那么就会报错我们要初始化这个成员变量需要用初始化列表才能解决初始化列表我们后续再来学习了解。特别说明C把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的原生数据类型 如int/char/double/指针等自定义类型就是我们使用class/struct等关键字自己定义的类型。举例说明注释代码语言javascriptAI代码解释#includeiostream using namespace std; class Date { public: //如果这里不自己显示构造函数编译器会自动生成一个无参的默认构造函数 //默认构造函数无参构造函数全缺省构造函数编译器默认生成的构造函数 //三种默认构造函数只能同时存在一个(第三个的原因不用多说前两个是因为构成重载但传参啥都不传时会歧义) //1.自己实现的无参数的构造函数(默认构造函数的一种) Date()//函数名和类名相同无返回值 { _year 1; _month 1; _day 1; } //2.带参构造函数(可以和1同时存在)(不能和3同时存在,函数签名相同构成了重定义)(不属于默认构造函数) Date(int year, int month, int day) { _year year; _month month; _day day; } ////3.全缺省构造函数(很好用结合了1,2的功能)(但是不能和1或者2同时存在)(默认构造函数的一种) //Date(int year 1, int month 1, int day 1) //{ // _year year; // _month month; // _day day; //} //总结:两种使用方案12或者直接用3 void Print() { cout _year / _month / _day \n; } private: int _year; int _month; int _day; }; int main() { Date d1; //会自动初始化调用1或者3不能同时存在(我上面给3注释掉了,因为2和3也不能同时存在) Date d2(2025, 9, 1);//会调用3不会有歧义 //Date d3();//这种写法是错误的 // 参考一下 Data func();你觉得它是对象还是函数呢 //注意:如果通过无参构造函数创建对象时对象后面不用跟括号。 //否则编译器无法区分这里是函数声明还是实例化对象 d1.Print();//1,1,1 d2.Print();//2025,9,1 return 0; }我们不写编译器默认生成的构造对内置类型初始化没要求(看注释)代码语言javascriptAI代码解释class Date { public: void Print() { cout _year / _month / _day \n; } private: //内置类型 int _year; int _month; int _day; }; int main() { Date d1; d1.Print();//打印出来的结果是随机值 return 0; }我们不写编译器默认生成的构造对于自定义类型成员变量要求调用这个成员变量的默认构造函数初始化注意注释)代码语言javascriptAI代码解释//我们不写编译器默认生成的构造对于自定义类型成员变量要求调用这个成员变量的默认构造函数初始化 //比如之前我们写过的两个栈实现队列 #includeiostream using namespace std; class Stack { public: Stack(int n4)//在这里还是要自己实现一个的不然会出跟上面一样的问题,我们来定义一个全缺省的 { _a (int*)malloc(n * sizeof(int)); if (_a nullptr) { perror(malloc fail!); exit(1); } _top 0; _capacity n; } private: //内置类型 int* _a; int _top; int _capacity; }; class MyQueue { public: //编译器默认生成MyQueue的构造函数调用了Stack的构造函数完成了两个成员的初始化 private: //自定义类型 Stack _pushst; Stack _popst; //内置类型但很奇怪混在这里它却能处理这里大家可以自己去试试 //int size 0; }; int main() { MyQueue q; return 0; }三.析构函数析构函数与构造函数功能相反析构函数不是完成对对象本身的销毁比如局部对象是存在栈帧的函数结束栈帧销毁他就释放了不需要我们管C规定对象在销毁时会自动调用析构函数完成对象中资源的清理释放工作。析构函数的功能类比我们之前Stack实现的Destroy功能就像Date没有Destroy其实就是没有资源需要释放所以严格说Date是不需要析构函数的。析构函数的特点析构函数名是在类名前加上字符 ~。无参数无返回值。 (这里跟构造类似也不需要加void)一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。对象生命周期结束时系统会自动调用析构函数。跟构造函数类似我们不写编译器自动生成的析构函数对内置类型成员不做处理自定类型成员会调用他的析构函数。还需要注意的是我们显示写析构函数对于自定义类型成员也会调用他的析构也就是说自定义类型成员无论什么情况都会自动调用析构函数。如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数如Date如果默认生成的析构就可以用也就不需要显示写析构如MyQueue但是有资源申请时(而且这里涉及内置类型的处理)一定要自己写析构否则会造成资源泄漏如Stack。一个局部域的多个对象C规定后定义的先析构。举例说明注意看注释代码语言javascriptAI代码解释#includeiostream using namespace std; class Stack { public: Stack(int n4)//在这里还是要自己实现一个的不然会出跟上面一样的问题,我们来定义一个全缺省的 { _a (int*)malloc(n * sizeof(int)); if (_a nullptr) { perror(malloc fail!); exit(1); } _top 0; _capacity n; } //析构函数(跟构造函数写法类似前面加个~) //在这里也需要自己定义,不然他也不处理内置类型,会造成内存泄漏 ~Stack() { if (_a) { free(_a); _a nullptr; } _top 0; _capacity 0; } private: //内置类型 int* _a; int _top; int _capacity; }; class MyQueue { public: //编译器默认生成MyQueue的构造函数调用了Stack的构造函数完成了两个成员的初始化 //编译器默认生成MyQueue的析构函数调用了Stack的析构函数释放的Stack内部的资源 //显示写析构也会自动调用Stack的析构 /*~MyQueue() {}*/ private: //自定义类型 Stack _pushst; Stack _popst; //内置类型但很奇怪混在这里它却能处理这里大家可以自己去试试 //int size 0; }; int main() { Stack s; MyQueue q; //析构不用显示写出来对象生命周期结束时自动调用 //后定义的先析构所以这里先析构q,再析构s.可以调试观察 return 0; }调试观察到当对象生命周期结束时确实都销毁掉了课堂检测答案是B解析如下四.括号匹配问题的优化(利用当前阶段所学知识)对比一下用C和C实现的Stack解决之前括号匹配问题isValid我们会发现有了构造函数和析构函数确实方便了很多不会再忘记调用Init和Destory函数了(下面的两个实现都需要借用一下栈这个数据结构先我这里就不展现出来了)。C语言实现代码语言javascriptAI代码解释bool isValid(char* s) { ST st; STInit(st); char* pi s; while (*pi ! \0) { if (*pi ( || *pi [ || *pi {) { STPush(st, *pi); } else { //右括号取栈顶元素进行匹配 //栈不为空才能取 if (STEmpty(st)) { STDestory(st); return false; } char top STTop(st); if ((top ( *pi ! )) || (top [ *pi ! ]) || (top { *pi ! })) { STDestory(st); return false; } //本次匹配就出栈 STPop(st); } pi; } //为空有效非空无效 bool ret STEmpty(st) ? true : false; STDestory(st); return ret; }构造和析构的C版本Stack实现代码语言javascriptAI代码解释bool isValid(char* s) { Stack st; char* pi s; while (*pi ! \0) { if (*pi ( || *pi [ || *pi {) { st.Push(st, *pi); } else { //右括号取栈顶元素进行匹配 //栈不为空才能取 if (st.Empty(st)) { return false; } char top st.Top(st); if ((top ( *pi ! )) || (top [ *pi ! ]) || (top { *pi ! })) { return false; } //本次匹配就出栈 st.Pop(st); } pi; } //栈为空返回真说明数量都匹配 左括号多右括号少匹配问题 return st.Empty(); }对比图