石狮做网站网站建设需注意点

当前位置: 首页 > news >正文

石狮做网站,网站建设需注意点,无锡企业网站制作,15个常见关键词#x1f44c;个人主页: 起名字真南 #x1f446;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 类的默认成员函数2 构造函数3 析构函数4 拷贝构造5 赋值运算符重载5.1 运算符重载5.2 赋值运算符的重载 1 类的默认成员函数 默认成员函数就是用户没有显示实现#xff0c;…个人主页: 起名字真南 个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 类的默认成员函数2 构造函数3 析构函数4 拷贝构造5 赋值运算符重载5.1 运算符重载5.2 赋值运算符的重载 1 类的默认成员函数 默认成员函数就是用户没有显示实现编译器自动生成的的成员函数称为默认成员函数 一个类我们不写的情况下会默认生成六个成员函数分别是构造函数析构函数拷贝构造赋值重载以及关于取地址对普通对象和const对象的重载。
2 构造函数 构造函数是特殊的成员函数构造函数的主要任务是在对象实例化时对其进行初始化对象。他的本质就是Stack和Date类中Init函数的功能而且构造函数可以自动调用的特点就完美的替代了Init函数。 构造函数的特点
函数名和类型名一致无返回值不需要写void 对象实例化时系统会自动调用对应的构造函数。构造函数可以重载如果类中没有定义构造函数那么编译器会自动生成一个无参的构造函数如果显示定义了就不会自己生成无参构造函数全缺省构造函数以及我们不写编译器自动生成的构造函数都叫做默认构造函数并且这三个函数不能同时存在同时存在虽然无参和全缺省构成了函数的重载但是调用函数时会存在歧义。总结不传实参就可以调用的构造函数统称为默认构造。我们不写编译器默认生成的构造函数对内置类型没有影响具体怎么初始化取决于编译器怎么初始化但是对于我们用class/struct来创建的自定义类型成员变量就需要调用他的构造函数进行初始化如果没有构造函数就会报错我们要初始化成员变量就需要用到初始化列表后面会解释 #includeiostreamusing namespace std; class Date { public://无参构造函数/Date(){_year 1;_month 1;_day 1;}///带参构造函数Date(int year, int month, int day){_year year;_month month;_day day;}//全缺省构造函数/Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}/void Print(){cout _year / _month / _day endl;} private:int _year;int _month;int _day; };int main() {Date d1; //调用无参的构造函数d1.Print();Date d2(2024, 10, 6); //调用带参的构造函数d2.Print();return 0; }如果我们将第一个无参构造函数和第三个全缺省构造函数注释掉编译器就会报错显示没有可用的构造函数。 当我们使用无参的构造函数进行初始化时后面不需要加否则编译器无法区分这里是函数声明还是实例化对象。 typedef int STLDateType; class Stack { public:Stack(int n 4){_a (STLDateType*)malloc(sizeof(STLDateType) * n);if (_a nullptr){perror(malloc fail!);return;}_top 0;_capacity n;} private:STLDateType* _a;size_t _top;size_t _capacity; };//两个Stack实现队列 class MyQueue { public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数完成了两个成员的初始化 private:Stack _STPush;Stack _STPop; }; int main() {MyQueue mq;return 0; }当一个类类型的成员变量是自定义类型的时候例如上面的代码MyQueue类的成员变量是自定义类型Stack类所以编译器不会生成MyQueue的默认构造函数需要我们自己来实现但是为什么我们明明没有写但是还是可以运行因为编译器默认生成MyQueue的构造函数调用了Stack的构造函数完成了两个成员的初始化。如果我们将Stack的构造函数注释掉就会报错。出现下面这种情况
3 析构函数 析构函数与构造函数的功能相反析构函数不是完成对对象本身的销毁比如局部对象创立是在栈帧如果函数结束那么栈帧就会被销毁不需要我们去管。C规定对象在销毁时会自动调用析构函数完成对对象中资源的清理和释放工作。析构函数的功能就类似与我们Stack类中Destory函数的功能而向Date类没有Destory就不需要析构函数。 析构函数的特点
析构函数的函数名是在类名的前面加上‘~’。无参数无返回值和构造函数类似一个类只能有一个析构函数如果未显示定义编译器就会自动生成一个默认析构函数函数声明周期结束会自动调用析构函数跟构造函数类似我们不写编译器自动生成的析构函数对内置类型的成员变量不做处理自定义类型成员会调用他自己的析构函数需要注意的是如果我们写了析构函数但是对于自定义类型还是会调用他自己的析构函数也就是说自定义类型成员变量无论什么时候都会调用自己的析构函数一个类中如果没有资源申请那么使用编译器自己生成的析构函数就可以比如Date类如果有资源申请像Stack/MyQueue类都需要自己写析构函数否则会造成资源泄露。一个局部域有多个对象C规定后定义的先析构。 #includeiostreamusing namespace std; typedef int STLDateType; class Stack { public:Stack(int n 4){_a (STLDateType*)malloc(sizeof(STLDateType) * n);if (_a nullptr){perror(malloc fail!);return;}_top 0;_capacity n;}~Stack(){cout ~Stack() endl;free(_a);_a nullptr;_top _capacity 0;} private:STLDateType* _a;size_t _top;size_t _capacity; };//两个Stack实现队列 class MyQueue { public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数完成了两个成员的初始化 private:Stack _STPush;Stack _STPop; }; int main() {Stack st;MyQueue mq;return 0; }从运行结果我们可以看到一共调用了三次析构函数分别是st一次和mq内的两次 4 拷贝构造 如果说构造函数的第一个参数是自身类类型的引用并且额外的参数都有默认值那么这个构造函数可以叫做拷贝构造函数拷贝构造也是一种特殊的构造函数。 拷贝构造的特点
拷贝构造函数是构造函数的重载拷贝构造的函数参数有且必须只有一个就是自身类类型的引用使用传值的方式编译器会报错因为会引发无穷递归。C规定自定义类型对象进行拷贝行为必须调用拷贝构造所以这里自定义类型传值传参和传值返回都会调用拷贝构造来完成若未显示定义拷贝构造编译器会自动生成拷贝构造函数自动生成的内置类型成员变量会进行值拷贝/浅拷贝一个字节一个字节的拷贝对自定义类型成员会调用他自己的拷贝构造像Date类型的成员变量都是内置类型可以不写拷贝构造直接使用编译器自己生成的拷贝构造函数但是如果是Stack类型虽然内部的成员变量都是内置类型但是其中的_a涉及到了资源的指向一起开辟如果使用浅拷贝会发生冲突所以需要我们自己实现并且将指向的资源同样进行拷贝。像MyQueue类型的虽然他的成员变量都是自定义类型但是在调用拷贝构造的时候也会调用Stack的拷贝构造所以也不用自己去实现。传值返回会产生一个临时对象调用拷贝构造传引用返回返回的是对象的别名引用没有产生拷贝。但是如果返回对象是当前局部域的临时对象当前函数结束时该局部对象也会销毁就会出现野引用的问题类似于野指针传引用返回可以减少拷贝但是一定要确定返回的对象不会因为当前函数结束而消失才可以使用。 #includeiostreamusing namespace std; class Date { public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}//编译报错 Date类的复制构造函数不能有Date类型的参数,但是可以有 Date 以及 Date* 类型//Date(Date d)Date(Date d){_year d._year;_month d._month;_day d._day;}Date(Date* d){_year d-_year;_month d-_month;_day d-_day;}void print(){cout _year - _month - _day endl;} private:int _year;int _month;int _day; };void func1(Date d) {cout d endl;d.print(); } Date func2() {Date tmp(2024, 10, 01);tmp.print();return tmp; } int main() {//C规定传值传参必须调用拷贝构造所以这里调用了拷贝构造Date d1(2024,10,07);//传值传参给d会调用拷贝构造使用传引用传参可以减少拷贝func1(d1);cout d1 endl;//传引用传参这里不是拷贝构造只是单纯的拷贝Date d2(d1);d1.print();d2.print();//这里也是拷贝构造Date d3 d1;d3.print();//拷贝构造用同类型的对象初始化而不是指针Date d4(d1);d4.print();//传引用返回的tmp在函数结束的时候就已经被销毁了所以返回的是野引用Date ret func2();ret.print();return 0; }class Stack { public:Stack(int n 4){_a (STDataType*)malloc(sizeof(STDataType) * n);if (_a nullptr){perror(malloc fail);}_capacity n;_top 0;}Stack(const Stack st){//需要对_a指向的资源同样进行拷贝_a (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (_a nullptr){perror(malloc fail);}memcpy(_a, st._a, sizeof(STDataType) * st._top);_capacity st._capacity;_top st._top;}void Push(STDataType data){if (_top _capacity){int newcapacity _capacity * 2;STDataType* tmp (STDataType*)realloc(_a, sizeof(STDataType) * newcapacity);if (tmp nullptr){perror(realloc fail);}_a tmp;_capacity newcapacity;}_a[_top] data;}~Stack(){cout ~Stack() endl;free(_a);_a nullptr;_top _capacity 0;} private:STDataType* _a;size_t _capacity;size_t _top; }; //用两个栈实现队列 class MyQueue { private:Stack push;Stack pop; }; int main() {Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);MyQueue m1;MyQueue m2 m1;return 0; }5 赋值运算符重载 5.1 运算符重载 当运算符被用于类类型对象时C允许我们通过运算符重载的方式指定新的含义重载运算符的参数个数和运算符本来的操作对象数量一样多一元操作符有一个参数二元运算符有两个参数。如果一个重载运算符函数是成员函数那么他的第一个参数是隐式指针this因此运算符重载作为成员函数时会少一个参数。. :: ?: . sizeof* 这五个运算符不能重载运算符重载是具有图书名字的函数他的名字是由operator加上后面定义的运算符共同构成。和其他函数一样他也有返回类型 函数体和参数列表重载运算符要注意后置多一个int类型的形参用来区分两个运算符重载‘’ 两个运算符的时候需要重载为全局函数如果是成员函数他的第一个默认类型是隐式类型this作为他的左操作数调用时就变成了 ‘ 输出的对象cout ’ 不符合使用习惯和可读性重载为全局函数只需要第一个参数类型时ostream/istream就可以了第二个参数位置为该类类型的对象。运算符重载后优先级和结合性要保持一致重载操作符必须有一个类类型的参数不能改变其原有的含义。 class A { public:void func(){cout A::func() endl;} }; typedef void(A::* PF)(); //成员函数指针类型int main() {//C规定成员函数要加才可以取到函数指针PF pf A::func;A obj;(obj.*pf)();//对象调用成员函数指针使用.运算符return 0; }//重载为全局函数会面临的问题 // 无法访问私有变量 // 解决办法 // 1 用public // 2 提供get函数 // 3 友元函数 // 4 重载为成员函数//public成员变量 bool operator(const Date d1, const Date d2) {return d1._year d2._year d1._month d2._month d1._day d2._day; } int main() {Date d1(2024, 10, 06);Date d2(2024, 10, 07);//显示调用operator(d1, d2);//编译器会转换成operator(d1, d2)d1 d2;return 0; }5.2 赋值运算符的重载 赋值运算符重载是一个默认成员函数用于完成两个已经存在的的对象直接拷贝赋值但这里和拷贝构造的区分拷贝构造用于一个对象拷贝初始化给另一个创建的对象。 赋值运算符重载的特点
赋值运算符重载是一个运算符重载必须重载为成员函数他的参数必须是当前的类类型建议加上const否则传值传参会有拷贝。有返回值建议写成当前类类型的引用引用返回可以提高效率有返回值的母体是为了可以连续赋值没有显式实现时编译器会⾃动⽣成⼀个默认赋值运算符重载默认赋值运算符重载⾏为跟默认构造函数类似对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉)对⾃定义类型成员变量会调⽤他的拷⻉构造 #includeiostreamusing namespace std; typedef int STDataType; class Date { public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}//编译报错 Date类的复制构造函数不能有Date类型的参数,但是可以有 Date 以及 Date
类型//Date(Date d)Date(Date d){_year d._year;_month d._month;_day d._day;}Date(Date* d){_year d-_year;_month d-_month;_day d-_day;}void print(){cout _year - _month - _day endl;}Date operator(const Date d1){if (this ! d1){_year d1._year;_month d1._month;_day d1._day;}return *this;} //private:int _year;int _month;int _day; }; int main() {Date d1(2024, 10, 06);Date d2(2024, 10, 07);//显示调用operator(d1, d2);//编译器会转换成operator(d1, d2)d1 d2;d2.print();d2 d1;d2.print();//注意这里不是赋值重载而是拷贝构造赋值重载只用于两个已经存在的对象// 而拷贝构造用于一个对象初始化另一个对象Date d3 d1;return 0; }