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

三水建设网站网站建设模板怎么用

三水建设网站,网站建设模板怎么用,商务网站建设体会,看网站的访问量文章目录 默认成员构造函数1. 构造函数1.1 概念1.2 特性 2. 析构函数2.1 概念2.2 特性 3. 拷贝构造函数3.1 概念3.2 特性 4. 运算符重载4.1 赋值重载4.2 自增自减重载4.3 取地址操作符重载 5. const成员函数6. 取地址重载 默认成员构造函数 上一节我们说过#xff0c;空类的大… 文章目录 默认成员构造函数1. 构造函数1.1 概念1.2 特性 2. 析构函数2.1 概念2.2 特性 3. 拷贝构造函数3.1 概念3.2 特性 4. 运算符重载4.1 赋值重载4.2 自增自减重载4.3 取地址操作符重载 5. const成员函数6. 取地址重载 默认成员构造函数 上一节我们说过空类的大小是1字节用来占位那空类是不是真的什么都没有呢 并不是C中的任何一个类都具有6个默认成员函数 即使它是空类它也拥有这6个默认成员函数下面我们依次介绍这些默认成员函数~ 1. 构造函数 1.1 概念 构造函数是当对象定义时编译器默认调用的用来完成对对象属性初始化的工作。 我们一开始写的class Date是这样 class Date {private:int _year;int _month;int _day;public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout _year _month _day endl;} };int main() {Date d1,d2;d1.Init(2023, 7, 21);d2.Init(2023, 7, 22);d1.Print();d2.Print(); }每定义一个对象都需要调用初始化函数初始化对象的属性如果我们哪一次忘记了初始化那么再访问该属性时便会访问随机值因此有没有一种办法定义对象时直接初始化呢这样就我们就不会忘记初始化了 我们可以定义构造函数帮我们完成这件事: 构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用以保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次。 1.2 特性 构造函数是特殊的成员函数需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并不是开空间创建对象而是初始化对象。 特征如下 函数名与类名相同无返回值类型对象实例化时编译器自动调用对应的构造函数构造函数可以重载本质就是我们可以写多个构造函数提供多种初始化方式 class Date的构造函数 class Date {private:int _year;int _month;int _day;public:Date(){_year 1;_month 1;_day 1;}void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout _year _month _day endl;} };int main() {Date d1;d1.Print(); }运行结果 构造函数在对象创建的时候自动调用(对象是编译器创建的不是构造函数创建的构造函数只负责对创建对象的属性进行初始化操作) 带参数的构造函数 //带参数的构造函数 Date(int year, int month, int day) {_year year;_month month;_day day; } int main() {Date d1;//调用无参构造函数Date d2(2023, 7, 21);//想要调用带参数的构造函数必须将括号写在对象名后面而不是类名后面d1.Print();d2.Print();// 注意如果通过无参构造函数创建对象时对象后面不用跟括号否则就成了函数声明// 以下代码的函数声明了d3函数该函数无参返回一个日期类型的对象// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)Date d3(); }**注意**语法规定调用带参数的构造函数必须将括号写在对象名后面 将带参构造函数和不带参构造函数合并为全缺省构造函数 //全缺省构造函数Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;} 运行结果 若定义了全缺省构造函数也定义了无参构造函数调用对象时没有指定构造函数的参数编译器不知道调用哪一个构造函数(全缺省还是无参)因而会报错(报错仅仅是因为编译器不知道调用哪一个而不是语法错误) class Stack class Stack { private:int* _a;int _top;int _capacity; public://带缺省参数的构造函数Stack(int capacity 4){_capacity capacity;_top 0;_a (int*)malloc(sizeof(int) * _capacity);assert(_a);}void Push(int val){if (_capacity _top){_capacity * 2;int* tmp (int*)realloc(_a, sizeof(int) * _capacity);if (tmp)_a tmp;}_a[_top] val;_top;}int Top(){return _a[_top - 1];}void Pop(){assert(_top 0);_top--;} };int main() {Stack st;st.Push(1);st.Push(2);st.Push(3);st.Push(4);st.Push(5);cout st.Top() endl;st.Pop();cout st.Top() endl; }运行结果 通过汇编观察定义对象时调用了构造函数 使用了构造函数可以让我们省去和C语言一样手动初始化栈并且使用默认参数可以让我们设定初始容量例如我们事先知道栈中要插入1000个元素我们就可以直接这么定义栈Stack st(1000);这省去了后续扩容的消耗。 如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数(后面皆称为系统默认构造函数)一旦用户显式定义编译器将不再生成。 class Date class Date { public:/*// 如果用户显式定义了构造函数编译器将不再生成Date(int year, int month, int day){_year year;_month month;_day day;}*/void Print(){cout _year - _month - _day endl;}private:int _year;int _month;int _day; };int main() {// 将Date类中构造函数屏蔽后代码可以通过编译因为编译器生成了一个无参的默认构造函数// 将Date类中构造函数放开代码编译失败因为一旦显式定义任何构造函数编译器将不再生成// 无参构造函数放开后报错error C2512: “Date”: 没有合适的默认构造函数可用Date d1;d1.Print();return 0; }我们没有定义构造函数因此定义d1时编译器会调用编译器生成的默认构造函数 打印结果 系统默认构造函数作用 系统默认构造函数对类的内置类型不做处理(语言自带的类型)有些编译器可能会进行处理但是C标准并没有这么规定 系统默认构造函数会去调用自定义类型变量的构造函数(union,struct,class等) class Date class Time {public:Time(){cout Time() endl;_hour 0;_minute 0;_second 0;}private:int _hour;int _minute;int _second; }; class Date {private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t; }; int main() {Date d;//调用Date类的系统默认构造函数return 0; }运行结果 解释 Date类定义对象时我们会自动调用Date类的系统默认构造函数系统默认构造函数对Date类的内置类型不做处理调用自定义类型Time默认构造函数因此_t的_hour,_minute,_second都会被初始化为0. 来看一段问题代码 class Time { public:Time(int hour, int minute, int second){cout Time() endl;_hour hour;_minute minute;_second second;} private:int _hour;int _minute;int _second; }; class Date { private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t; }; int main() {Date d;//调用Date类的系统默认构造函数return 0; }运行结果 **解释**Date只有系统默认构造函数定义对象时调用系统默认构造函数系统默认构造函数调用Time类的默认构造函数但是我们已经定义了Time带参数的构造函数因此Time类不具有默认构造函数因而程序出错。 注意C11针对系统默认构造函数不会对内置类型进行处理做了一个补丁C11后允许将内置成员变量在类中声明时给定默认值 //C11给定缺省值 class A { public:void Print(){cout _a _c endl;} private:int _a 1;char _c a; };int main() {A a;//调用系统默认构造函数给_a初值1,_c初值aa.Print();return 0; }调用时可以不传参的构造函数称为默认构造函数默认构造函数有3类:系统默认构造函数自定义的无参构造函数全缺省的构造函数。 //默认构造函数 class A {public://自定义无参构造函数A(){cout A()\n;}//全缺省构造函数A(int a 1, int b 2){_a a;_b b;cout A(int a, int b)\n;} private:int _a;int _b; };int main() {A a;//无法编译通过因为不知道调用哪一个默认构造函数return 0; }2. 析构函数 2.1 概念 析构函数与构造函数的功能相反。**析构函数的作用是在对象销毁前后执行对象中资源清理工作**对象销毁不是析构函数完成的就像对象的创建不是构造函数完成的一样 2.2 特性 析构函数名是在类名前加上字符~。无参数无返回值类型。一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。注意析构 函数不能重载 对象生命周期结束时C编译系统系统自动调用析构函数 class Stack typedef int DataType; class Stack { public:Stack(size_t capacity 3){_array (DataType*)malloc(sizeof(DataType) * capacity);if (NULL _array){perror(malloc申请空间失败!!!);return;}_capacity capacity;_size 0;}void Push(DataType data){// CheckCapacity();_array[_size] data;_size;}// 其他方法...~Stack(){if (_array){free(_array);_array NULL;_capacity 0;_size 0;}} private:DataType* _array;int _capacity;int _size; }; void TestStack() {Stack s;s.Push(1);s.Push(2); }有了析构函数就不需要我们自己手动销毁栈了 编译器生成的默认析构函数作用 对于内置类型不做处理对于自定义类型成员调用自定义类型的析构函数 class Time { public:Time(){cout Time() endl;}~Time(){cout ~Time() endl;} private:int _hour;int _minute;int _second; }; class Date { public:Date(){cout Date() endl;}~Date(){cout ~Date() endl;} private:// 基本类型(内置类型)int _year 1970;int _month 1;int _day 1;// 自定义类型Time _t; }; int main() {Date d;return 0; }运行结果 如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如class Date有资源申请时一定要写否则会造成内存泄漏比如class Stack 3. 拷贝构造函数 3.1 概念 创建对象时用当前已存在的对象初始化新对象称为拷贝。 拷贝构造函数只有单个形参**该形参是对本类类型对象的引用(**一般常用const引用)在用已在的类类型对象创建新对象时由编译器自动调用。 c规定任何自定义类型的拷贝都会调用拷贝构造函数 calss Stack 和 class Date void fun1(Date d1) {} void fun2(Stack st1) {} int main() {Stack st;Date d;fun1(d);//传参时调用Date类型的默认拷贝构造函数fun2(st);//传参时调用Stack类型的默认拷贝构造函数return 0; }运行结果 调用Stack拷贝构造函数时会出现问题原因我们后面解释。 3.2 特性 拷贝构造函数是构造函数的一个重要重载形式 拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷递归调用。 class Date { public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}// Date(const Date d) // 正确写法Date( Date d)//错误写法---拷贝构造函数的参数不能是Date否则会发生无穷递归{_year d._year;_month d._month;_day d._day;} private:int _year;int _month;int _day; }; int main() {Date d1;Date d2(d1);//传参时将d1拷贝给d:Date d(d1)此时会调用拷贝构造函数,拷贝构造函数右会将d1拷贝给d:Date d(d1)引发无穷递归 }[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5SrIgELR-1690679807038)(images/image-20230723133609228.png)] 传值拷贝引发的无穷递归 若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。 class Time { public:Time(){_hour 1;_minute 1;_second 1;}Time(const Time t){_hour t._hour;_minute t._minute;_second t._second;cout Time::Time(const Time) endl;} private:int _hour;int _minute;int _second; }; class Date { private:// 基本类型(内置类型)int _year 1970;int _month 1;int _day 1;// 自定义类型Time _t; }; int main() {Date d1;Date d2(d1);return 0; }运行结果 默认拷贝构造函数的作用 对内置类型实现字节序拷贝 对自定义类型会调用自定义类型的拷贝构造函数 c拷贝自定义对象时为什么要使用拷贝构造函数和c语言一样全部按字节序拷贝可以吗 不可以,例如上述class Stack若使用默认拷贝构造函数则对于内置类型就是字节序拷贝这就会出现内存问题我们来回顾一下上述代码 class Stack { public:Stack(size_t capacity 3){_array (DataType*)malloc(sizeof(DataType) * capacity);if (NULL _array){perror(malloc申请空间失败!!!);return;}_capacity capacity;_size 0;}void Push(DataType data){// CheckCapacity();_array[_size] data;_size;}~Stack(){if (_array){free(_array);_array NULL;_capacity 0;_size 0;}} private:DataType* _array;int _capacity;int _size; }; void fun1(Date d1) {} void fun2(Stack st1) {} int main() {Stack st;Date d;fun1(d);//传参时调用Date类型的默认拷贝构造函数fun2(st);//传参时调用Stack类型的默认拷贝构造函数return 0; }如果是值拷贝。那么拷贝后st的成员_a和st1的成员_a都指向同一块空间 当st1对象销毁时会调用析构函数析构函数会将st1对象_a成员指向的空间释放此时st对象的_a成员就是野指针了当st对象销毁时调用析构函数释放野指针指向的空间就会引发内存问题。 因此像Stack的类我们就需要自己重定义拷贝构造函数实现深拷贝 //自定义拷贝构造函数实现深拷贝Stack(const Stack st){_array (DataType*)malloc(sizeof(DataType) * st._capacity);if (nullptr _array){perror(malloc fail);exit(-1);}memcpy(_array, st._array, sizeof(DataType) * st._size);_capacity st._capacity;_size st._size;} int main() {Stack st1;st1.Push(1);st1.Push(2);st1.Push(3);Stack st2(st1);//调用拷贝构造函数完成深拷贝return 0; }运行结果 拷贝构造函数调用场景 使用已存在的对象创建新对象函数参数为类类型对象函数返回值为类类型对象 class Date { public:Date(int year, int minute, int day){cout Date(int,int,int): this endl;}Date(const Date d){cout Date(const Date d): this endl;}~Date(){cout ~Date(): this endl;} private:int _year;int _month;int _day; }; Date Test(Date d) {Date temp(d);return temp; } int main() {Date d1(2022, 1, 13);//拷贝构造函数Test(d1);return 0; }4. 运算符重载 C为了增强代码的可读性新增了运算符重载运算符重载是具有特殊函数名的函数也具有返回值类型函数名字以及参数列表返回值的类型与普通函数相同。 函数名为operator操作符 函数原型为返回值类型 operator操作符(参数列表) 注意 不能通过连接其它符号来创建新的操作符比如operator重载操作符必须有一个参数为自定义类型参数用于内置类型的运算符其含义不能改变例如内置类型的,不能改变其含义用于类成员函数重载时其形参看起来总是比操作数目少1因为成员函数的第一个参数为隐藏的this指针.* :: sizeof ?: .这五个操作符不可以重载 //运算符重载 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}//运算符重载为成员函数可以访问类的私有成员bool operator(const Date d2)//实际上(Date* const this, const Dated2){return _year d2._year _month d2._month _day d2._day;}private:int _year;int _month;int _day; }; //bool operator(const Date d1, const Date d2) //{ // return d1._year d2._year d1._month d2._month d1._day d2._day;//由于类外部不能直接访问private成员 // //可以通过友元函数解决这里先将运算符重载作为成员函数. //}int main() {Date d1(2023, 7, 23);Date d2(2023, 7, 21);cout d1.operator(d2); 运行结果 这种调用和调用普通成员函数的方法一样看不出可读性因此调用运算符重载时可以像内置类型一样直接使用运算符。 需要注意的是d1d2会被编译器转换为d1.operator(d2) d2d1会被编译器转换为d2.operator(d1)即操作符的左操作数是调用运算符重载的对象也是his指针指向的对象 4.1 赋值重载 a.赋值运算符重载格式 参数类型const T,引用传递可以提高传参效率返回值类型T,有返回值是为了支持连续赋值返回引用是为了提高返回的效率。检查是否给自己赋值返回*this:要符合连续赋值的含义 class Date { public:Date(int year 2023, int month 7, int day 28){_year year;_month month;_day day;}Date operator(const Date date){if (this ! date){_year date._year;_month date._month;_day date._day;}return *this;//出了函数后*this还存在可以返回引用}void Print(){cout _year - _month - _day endl;} private:int _year;int _month;int _day; }; int main() {Date d1;Date d2(2023, 7, 20);d2 d1;//调用赋值重载,转化为d2.operator(d1);d2.Print();return 0; }运行结果 b.编译器会默认生成赋值重载成员函数 如果我们没有显示定义赋值重载则编译器会默认生成一个复制重载函数并且该函数完成字节序拷贝类似于默认生成的拷贝构造函数。对于class Stack这种类型使用默认的赋值重载函数将会出错需要使用自定义的赋值重载函数和自定义的拷贝构造函数。 c.赋值运算符只能重载为成员函数不可以重载为全局函数 上面刚说过每一个类都会有自己的默认赋值重载函数如果我们将赋值重载写为全局函数那么该类就会生成一个默认重载函数调用时不知道调用类的赋值重载还是调用全局的赋值重载。 d.Date d; Date d2 d属于拷贝构造函数不会调用赋值重载 **注意**如果类中未涉及到资源管理赋值运算符是否实现都可以一旦涉及到资源管理则必须要实现。 4.2 自增自减重载 自增自减分为前缀自增、后缀自增、前缀自减、后缀自减。前后缀自增都是单目运算符那么重载时如何区分重载的对应运算符是前缀还是后缀呢 C规定后缀重载时多增加一个int类型的参数但调用该函数时不需要显示传递参数编译器会自动传递 class A { public:A(){_a 1;}A operator()//重载前缀{_a;return *this;}A operator(int)//重载后缀{A tmp(*this);_a;return tmp;}void Print(){cout _a endl;} private:int _a; }; int main() {A a;a;//编译器转换为a.operator()a.Print();a;//编译器转换为a.operator(0);a.Print(); }可以看见对于自定义类型前置不需要调用拷贝构造函数后置需要调用2次拷贝构造函数。因此对于自定义类型来说前置的效率更高。 总结 前缀重载运算符效率高定义后缀重载时参数列表给出一个int类型参数作为标记 4.3 取地址操作符重载 取地址及const取地址操作符重载 5. const成员函数 调用成员函数时实际上会传递this指针this指针指向的是当前对象我们知道this指针在函数参数中不可以显示传递接受那么如果我们要求当前对象的相关信息不可以被更改怎么办呢我们需要用const修饰this指针但是这里的const应该放在哪里呢我们规定const修饰成员函数时const应该放在函数参数列表最后面。 ​ class Date { public:Date(int year 2023, int month 7, int day 28){_year year;_month month;_day day;}Date operator(const Date date)//只能重载为成员函数{if (this ! date){_year date._year;_month date._month;_day date._day;}return *this;//出了函数后*this还存在可以返回引用}void Print()const//const成员函数表明调用的对象信息不可以更改{cout _year - _month - _day endl;} private:int _year;int _month;int _day; }; int main() {Date d;d.Print();//权限缩小const Date d1;d1.Print();//权限平移return 0; }运行结果 注意const成员函数可以和普通成员函数同时存在,调用时优先匹配最合适的 class Date { public:Date(int year 2023, int month 7, int day 28){_year year;_month month;_day day;}void Print()const //const成员函数表明调用的对象信息不可以更改--参数为const的成员函数既可以打印const对象又可以打印非const对象{cout _year - _month - _day endl;cout void Print()const\n;}//const成员函数重载void Print()//参数为没有被const修饰的成员函数只能打印非const对象{cout _year - _month - _day endl;cout void Print()\n;} private:int _year;int _month;int _day; }; int main() {Date d1;const Date d2;d1.Print();//void Print()d2.Print();//void Print() constreturn 0; }理论上void Print() const可以打印非const对象但是这里重载了非const的Print函数因此d1.Print()时会优先调用最佳匹配的也就是void Print() 关于const成员函数需要知道的几点 const对象不可以调用非const成员函数非const对象可以调用const成员函数const成员函数内部不可以调用非const成员函数非const成员函数内部可以调用const成员函数 6. 取地址重载 //取地址重载 class Date { public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}//普通取地址重载Date* operator(){return this;}//const取地址重载const Date* operator()const{return this;} private:int _year;int _month;int _day; }; 取地址运算符一般不会重载使用默认生成的即可除非你不想让别人获取对象的地址
http://www.dnsts.com.cn/news/174685.html

相关文章:

  • 家装网站建设案例更改网站图片
  • 网站表单ui设计交付物都包含哪些
  • 网站开发找谁网站开发包含上线吗
  • 建设公司设计公司网站响应式网页网站设计
  • 政务网站网上调查怎么做制作人
  • 海南公司注册网站软件定制公司排名
  • 网站建设合同书(范本)wordpress+登陆图标
  • 做网站 需求怎么写焦作专业网站建设费用
  • 什么网站可以自己做房子设计图乐都企业网站建设公司
  • 重庆做网站开发的公司百度推广费用怎么算
  • 汤阴有没有做网站的公司重庆电子工程职业学院教务网
  • 乐达网站建设怎样申请网络域名
  • 网站建设财务怎么入账何鹏seo
  • 安徽易企建站wordpress版本对应php版本
  • 做公司的网站大概多少钱网站的结构设计
  • 推广网站要注意什么如何进行电商营销推广
  • 商城网站如何优化电影网站源码怎么做的
  • seo综合查询重庆企业网站seo
  • 网站建设学什么书小程序代理加盟前景
  • 专门做图片是网站wordpress 足迹地图
  • 深圳市建设工程造价管理站国内知名摄影网站
  • 做电影网站用什么源码贵州建设职业技术学院网站查成绩
  • 专业做网站费用网站搭建需要服务器吗
  • 网站维护与排名内部网站建设计划
  • 怎么分析一个网站定制家具全屋定制
  • 游戏公司招聘网站河池公司做网站
  • 淄博企业网站制作做公司网站 国外系统
  • 怀柔高端网站建设上海工商网查询官网
  • 三站合一 网站建设iis网站301重定向
  • 淘宝客网站需要备案吗wordpress用户投稿单页