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

高端网站改版顾问贵阳有做网站的公司吗

高端网站改版顾问,贵阳有做网站的公司吗,网站开发 税率,贵州热点新闻事件C---类和对象#xff08;上#xff09; 类和对象上#xff0c;我们把类怎么定义#xff0c;怎么访问等等都讲的差不多了#xff0c;那类呢#xff0c;还有一些其他的东西。 1.类的默认成员函数 什么是默认成员函数 我们不写#xff0c;编译器会自动生成的就是好默认成…C---类和对象上 类和对象上我们把类怎么定义怎么访问等等都讲的差不多了那类呢还有一些其他的东西。 1.类的默认成员函数 什么是默认成员函数 我们不写编译器会自动生成的就是好默认成员函数 默认成员函数总共有6个 我们重点学前4个 初始化和清理 构造函数 是啥 构造函数是一个特殊的成员函数。它虽然名字叫构造但是它不是用来创建对象的。 为什么它不是创造对象呢 比如说我在这儿定义一个对象。 这个对象的空间不是由我们去开的。它是属于一个局部对象。 局部对象他就在这个栈帧里面编译的时候就计算好了开栈帧的时候就把它开好了。 如果它是动态开辟的那是调了一个malloc函数去完成的它有单独的函数当然C里面是调一个叫new的函数来完成的。 如果是全局的静态的这些也一样都是系统开的。 所以对象开空间不归我们管。 它其实是用来初始化对象的对象实例化的时候完成初始化。再简单一点理解就是我们以前写的Init函数的功能。 也就是说C里面期望用构造函数替代Init函数。 那它凭什么替代Init函数呢因为它比Init函数更方便。 特点 构造函数有如下特点 1. 函数名与类名相同 2. ⽆返回值(返回值啥都不需要给也不需要写void不要纠结C规定如此) 3. 对象实例化时系统会⾃动调⽤对应的构造函数相比Init函数最大的特点 4. 构造函数可以重载 也就是说它有不同的参数我们可以定义多种初始化的方式。 5. 如果类中没有显式定义构造函数则C编译器会⾃动⽣成⼀个⽆参的默认构造函数⼀旦⽤⼾显式定义编译器将不再⽣成 6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数都叫做默认构造函数。但是这三个函数有且只有⼀个存在不能同时存在。⽆参构造函数和全缺省构造函数虽然构成函数重载但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫默认构造实际上⽆参构造函数、全缺省构造函数也是默认构造总结⼀下就是不传实参就可以调⽤的构造就叫默认构造。 7. 我们不写编译器默认⽣成的构造对内置类型成员变量的初始化没有要求也就是说是是否初始化是不确定的看编译器。对于⾃定义类型成员变量要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量没有默认构造函数那么就会报错我们要初始化这个成员变量需要⽤初始化列表才能解决初始化列表我们下个章节再细细讲解。 最后一点在这个地方得提一点点小概念 C把类型分成内置类型(基本类型)和⾃定义类型。 内置类型就是语⾔提供的原⽣数据类型 如int/char/double/指针等 。 ⾃定义类型就是我们使⽤class/struct等关键字⾃⼰定义的类型。 总结 大多数情况构造函数都需要我们自己去实现少数情况类似MyQueue且Stack有默认构造时MyQueue自动生成就可以用。应写尽写。 构造函数怎么写呢 首先第一个构造函数是这么写的。 然后它会自动调用 这是啥意思呢 以前你写Init函数你是不是在这儿定义一个对象要调用显式的再用d1去调用一下Init                万一你忘记调用了那是不是就没有初始化 现在这个地方不需要显式的调用它会自动调用。 它的调用也和我们之前的Init函数不一样。 调用无参的不能这么写这么写编译会报错。 这个对象是没有定义出来的。 为什么这个东西和函数声明区分不开所以规定不能这么写。 第一个我们定义了一个无参的我们说构造函数可以重载。 我们再来第二个 我们可以定义一个带参的。 还可以构造一个全缺省的 这个全缺省的和这个无参的能不能同时存在啊 不能我们之前说过了会产生调用歧义。 但是换一个角度写了全缺省了还需要写这个无参的吗不需要。 我们啥都不写编译器是不是会生成一个默认的无参构造 那我们不写的时候这个默认的无参构造它会干什么事情呢 它对内置类型成员变量的初始化没有要求。内置类型到底初不初始化不确定看编译器C标准没有规定。 早期的编译器都不初始化建议把它当成内置类型不处理。 对于⾃定义类型成员变量要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量没有默认构造函数说明默认生成的构造函数不行对于这个自定义类型成员没有完成初始化那么就会报错。 报错之后怎么办呢就只能我们自己显式去初始化。我们要初始化这个成员变量需要⽤初始化列表才能解决初始化列表我们下个章节再细细讲解。 析构函数 是啥 析构函数和构造函数功能相反。 它不是完成对对象本身的销毁。 这个MyQueue和这个栈这些对象的空间开辟是编译的时候系统就开好的。 它们在这儿开空间的时候是编译器编译的时候算好它们建立main函数栈帧一把就开好了。 那他们对象本身的空间也是在这个函数结束了以后栈帧销毁一把是不是就销毁了 它完成的是对象中的资源清理释放工作。它类似于我们的Destory。 严格来说有些类是不需要析构函数的。 为什么呢 因为没有资源清理就不需要析构。 比如说我们的Date类它就不需要析构。 但是我们尝试写一写带大家看一下。 我们来看一下 构造 析构 如果有多个对象先析构谁呢 后定义的先析构 栈帧这个东西和数据结构那个栈的性质还是一样它要求后进先出。所以这儿是st2先析构。 特点 1. 析构函数名是在类名前加上字符 ~ ~ 这个符号是不是我们位运算学的按位取反啊 构造是初始化析构的功能和它是相反的所以它就在类名前面加个~。意思是说它和构造函数的功能是相反的 2. ⽆参数⽆返回值。 (这⾥跟构造类似也不需要加void) 3. ⼀个类只能有⼀个析构函数。若未显式定义系统会⾃动⽣成默认的析构函数。 4. 对象⽣命周期结束时系统会⾃动调⽤析构函数。 5. 跟构造函数类似我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理⾃定类型成员会调⽤他的析构函数。 6. 还需要注意的是我们显⽰写析构函数对于⾃定义类型成员也会调⽤他的析构也就是说⾃定义类型成员⽆论什么情况都会⾃动调⽤析构函数。 MyQueue需不需要显式写析构-----不需要 为什么不需要 我们不写Myqueue的析构自动生成的析构函数会去调用两个栈的析构。 如果我在这儿写一个这会不会内存泄漏呢 不会 所以自定义类型成员你不写默认生成的会去调它的析构你写了对于自定义类型成员你也不需要显式析构祖师爷就生怕有人在这个地方胡搞 显式写析构也会⾃动调⽤Stack的析构 那我要是不写这个呢 那就内存泄漏了 7. 如果类中没有申请资源时析构函数可以不写直接使⽤编译器⽣成的默认析构函数如Date如果默认⽣成的析构就可以⽤也就不需要显⽰写析构如MyQueue但是有资源申请时⼀定要⾃⼰写析构否则会造成资源泄漏如Stack。 8. ⼀个局部域的多个对象C规定后定义的先析构。 构造函数和析构函数最大的特点 自动调用 拷贝复制 拷贝构造函数 拷贝构造它是一个特殊的构造。它这个构造的要求是 第一个参数是自身类型的引用 为什么第一个参数必须得是类类型的引用呢 首先我们来看它如下的一些特点 那它的基本特点呢构造函数有的它肯定都有 1.拷贝构造函数是构造函数的一个重载 2.拷贝构造的第一个参数必须是自身类类型对象的引用使用传值方式编译器直接报错因为语法逻辑上会引发无穷递归调用 为什么它第一个参数必须是自己这个类类型呢 这个很好理解 因为它是完成当前对象的一个拷贝嘛。 我们说拷贝构造构造这个地方所想表达的一个意思是什么呢想表达的意思其实                  是初始化它是想拿自己这个类型去初始化自己 我们用date类来理解一下 我们首先来看这样的几个方面 我想拷贝一下当前这个对象怎么拷贝呢 我们平时创建一个日期我们肯定是用年月日初始化是不是                                                 但是我有没有可能用同类型的对象去初始化 是不是可以啊                                                                                                                                                                                                                                                                                   那这个地方调的就是一个普通构造 这个地方调的这个构造我们就把它叫做拷贝构造 那拷贝构造必须得怎么写呢 必须这么写 它是一个构造函数所以函数名和类名相同。 参数 如果还有剩下参数你必须得有缺省值 不过一般只有一个参数 我们的第一个参数必须是自身类类型对象的引用使用传值方式编译器                                    直接报错 使用传值方式编译器直接报错 那我们来看一下为什么必须是引用呢 得换一个样例来讲一讲 首先你得看明白第一个问题C规定函数的传值传参要调用拷贝                                             构造 C语言没有这样的规定嘛 C语言整型的话就按四个字节一个字节一个字节的拷贝                                                           过去 结构体传就是直接按字节拷贝过去了 你现在是不是要调用Func1啊但是要调用Func1这个函数你                                                    是不是要先传参啊 以前的传参就是直接传值拷贝嘛 但现在要调用拷贝构造 所以我按F11不是走进Func1而是走到拷贝构造去了 走完拷贝构造又回来了 回来的意思是什么 因为我要调用这个函数得先传参啊传参是不是形成了一                                                       个拷贝构造啊拷贝构造完了以后传参完成了我再按F11 这本质相当于是不是两个函数调用啊 这个时候再来理解如果我们在这个地方用传值是不是会形成所                                          谓的无穷递归 刚才的路径是这样的 灰色的虚线是正确的路径 那我用一个引用这个时候传参还会不会形成一个拷贝构                                                             造                                                                                                                                       不会 建议加上const 这样可以保护形参不被改变 做个简单的比方 假设我想写这样一个逻辑 但是我不小心写错了 这个时候会发生什么 我d1本来是去初始化d2的结果我d1自己变成随机值了自己被改                                             了 所以传引用的时候如果你不想改变实参那就把const死死的加                                            上 再换一个角度 我们以后对于自定义类型传参的时候还建议去用传值传参                                                       吗 是不是不建议啊 因为传值传参调用拷贝构造这是不是太费劲了所以我们建                                                  议传引用 3. C规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造所以这⾥⾃定义类型传值传参和传值返回都会调⽤拷⻉构造完成。 4. 若未显式定义拷⻉构造编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉)对⾃定义类型成员变量会调⽤他的拷⻉构造。 这个部分跟构造函数不一样了 构造函数对于内置类型初始化是不确定的 析构函数对于内置类型是不是不处理啊 拷贝构造内置类型它也管 ⾃动⽣成的拷⻉构造对内置类型成员变量会完成值拷⻉/浅拷⻉ 理解一下值拷贝/浅拷贝 比如说你是一个日期类你有年月日值拷贝/浅拷贝指的是一个字节一个字节                      的拷贝就像memcpy拷贝一样 假如d2要拷贝d1。年月日如果按内存对齐算好是不是12个字节啊我把你一                      个字节一个字节的拷贝过去。 那日期类型是不是不需要写拷贝构造啊 我不写它也能完成值拷贝 自动生成完成值拷贝一个字节一个字节完成拷贝 这个东西某种程度上也是为了兼容C语言 C语言结构体传参会不会完成拷贝 C语言是不是没有拷贝构造这样的说法但是大家想一想一个问题C语言传值传                     参的时候会不会完成拷贝对于结构体 内置类型肯定是完成拷贝嘛比如你一个整型是不是拷贝过去了 结构体也会拷 它其实完成的跟这儿一样只不过他是一个字节一个字节的拷构造函数是                             一个变量一个变量的拷贝 对于自定义类型会去调用它的拷贝构造 5. 像Date这样的类成员变量全是内置类型且没有指向什么资源编译器⾃动⽣成的拷⻉构造就可以完成需要的拷⻉所以不需要我们显⽰实现拷⻉构造。像Stack这样的类虽然也都是内置类型但是_a指向了资源编译器⾃动⽣成的拷⻉构造完成的值拷⻉/浅拷⻉不符合我们的需求所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)。像MyQueue这样的类型内部主要是⾃定义类型Stack成员编译器⾃动⽣成的拷⻉构造会调⽤Stack的拷⻉构造也不需要我们显⽰实现MyQueue的拷⻉构造。这⾥还有⼀个⼩技巧如果⼀个类显⽰实现了析构并释放资源那么他就需要显⽰写拷⻉构造否则就不需要。 像Date这样的类成员变量全是内置类型且没有指向什么资源编译器⾃动⽣成的拷⻉构造就可以完成需要的拷⻉所以不需要我们显⽰实现拷⻉构造。像Stack这样的类虽然也都是内置类型但是_a指向了资源编译器⾃动⽣成的拷⻉构造完成的值拷⻉/浅拷⻉不符合我们的需求所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)。 这个栈是不是都是内置类型啊 那按我们之前写的我有没有写拷贝构造我不写我没写拷贝构造会不会完成拷贝会 我们来看一下又没有完成拷贝完成了 但是程序崩了 如果我们仔细观察这里的对象其实我们已经发现了一些问题 栈 这个类和我们前面讲的类有所不同因为日期类的内置类型没有指向什么资源栈这个类是不是指向了单独的类空间的资源的啊 那我有12个字节你也有12个字节那我把我的12个字节按值拷贝拷贝给给你有没有问题 是有问题的 我指向一段空间这一段空间是0x014c94b8 那你在这个地方直接按字节拷贝也指向它 这个时候会导致什么问题啊 析构的时候这段空间是不是析构两次啊? 那一块儿空间能不能析构两次呢不能所以程序崩溃 问题就出来了 所以对于栈这样的类拷贝构造得我们自己写 对于栈这样的类浅拷贝值拷贝是不是不行啊得我们自己写 那我们自己得怎么写 我自己写的话我知道这是浅拷贝我得识别这样的情况像栈这样里面有资源的是不是啊 我就不能让两个指针指向同一个空间 我拷贝你 是我的top和capacity跟你一样跟你一样但我的_a不能和你一样我是不是得有自己的空间啊在这个地方不能照本宣科 深拷贝再深一个层次去拷贝不仅拷贝这个值还要拷贝这个指针指向的资源 所以默认的拷贝构造并不能满足所有的需求 我们再来回顾一个王炸型的问题大家理解一下 C为什么规定传值传参必须调用拷贝构造 祖师爷为什么要做这件事情 如果跟C语言一样这个地方会发生什么假设这是一个C语言的栈的结构体 1. 我把我的栈拷贝给你那我们俩是不是就指向同一块资源了里面改变是不是也会影响外面啊 你以为我是传值他是我的拷贝他的改变不影响我但它实际会影响你。 2. 它Destory你再Destory行不行 不行 如果调用拷贝构造我st的改变不会影响st1,是不是更合理啊 这也从另一个角度警示我们-------自定义类型用传值传参还好不好 不好 这儿传值传参我本来里面啥事都没干调用这个拷贝构造的代价很大除了说直接赋制以外还要开这个栈的空间还要去堆上开空间 假设我这个栈有10000个数据那是不是更扯淡了 所以这些地方都不断的在告诫我们 函数传参尽可能用引用 如果不改变尽可能用const 所以你不要觉得C语言那个是更好的祖师爷规定传值传参必须调用拷贝构造是为了填C语言的坑C语言在这些部分是很坑很坑的 像MyQueue这样的类型内部主要是⾃定义类型Stack成员编译器⾃动⽣成的拷⻉构造会调⽤Stack的拷⻉构造也不需要我们显⽰实现MyQueue的拷⻉构造。 MyQueue就像孩子一样 平时的吃穿用度你好像都没花钱但你的爸妈花了钱 MyQueue有现在的生活啥都不用做那是靠栈 所以真的要完成资源拷贝总有人负重前行 这⾥还有⼀个⼩技巧 如果⼀个类显⽰实现了析构并释放资源那么他就需要显⽰写拷⻉构造否则就不需要。 一个类如果你实现了析构函数的话说明你有资源需要清理那这时基本上就需要自己实现拷贝构造嗷 拷贝构造还可以这样写 6. 传值返回会产⽣⼀个临时对象调⽤拷⻉构造传值引⽤返回返回的是返回对象的别名(引⽤)没有产⽣拷⻉。但是如果返回对象是⼀个当前函数局部域的局部对象函数结束就销毁了那么使⽤引⽤返回是有问题的这时的引⽤相当于⼀个野引⽤类似⼀个野指针⼀样。传引⽤返回可以减少拷⻉但是⼀定要确保返回对象在当前函数结束后还在才能⽤引⽤返回。 之前引用部分的时候我们说过传引用返回我们讲一点点剩下的是不是后面再讲啊 传值返回会产⽣⼀个临时对象调⽤拷⻉构造 传引⽤返回返回的是返回对象的别名(引⽤)没有产⽣拷⻉。 但是如果返回对象是⼀个当前函数局部域的局部对象函数结束就销毁了那么使⽤引           ⽤返回是有问题的这时的引⽤相当于⼀个野引⽤类似⼀个野指针⼀样 我们之前看到的都是传参的问题现在我们来看一看传值返回的问题 我们调用Func2传值返回这个地方会发生什么 这儿是不是会产生一个对象的拷贝啊 为了让这个地方的拷贝减少可以用引用返回 传值返回和传引用返回的区别是 传值返回它不会返回st,它会返回st的拷贝这个拷贝的临时对象不受Func2生命周                     期的影响 有的时候会涉及到编译器优化的问题在类和对象下再来讲 如果你不想让这个拷贝发生加一个引用 加一个引用是不是就减少拷贝了返回st的别名 但是这个时候会发生什么 st是不是已经销毁了 调用析构函数里面是不是全是野指针啊 那这个时候是不是就非常的扯淡啊 有没有完成拷贝啊没有 为什么没有完成拷贝呢 最核心的原因是因为st都已经销毁了 我们把这个样例从另一个角度再来看一看 编译器也会给你报一个警告 从当前这个程序来看这样才是正确的 如果还有额外的参数的话必须得有默认值 赋值重载 后面讲 后两个         取地址重载 普通对象 const对象 其实实际当中也不止6个C11以后又增加了两个移动构造和移动赋值。 但这个阶段我们不讲这个东西。 有人会说“我们不写编译器不是会自动生成吗那这东西有啥可学的啊” 实际上不是这样的。 这几个函数为什么难学呢 就是因为它们之间 1.知识点很多 2.它们之间又千丝万缕的关系有很多要注意的点。 我们要从两个方面去学习 第⼀我们不写时编译器默认⽣成的函数⾏为是什么是否满⾜我们的需求。 它只有部分场景满足我们的需求有部分场景不满足我们的需求 第⼆编译器默认⽣成的函数不满⾜我们的需求我们需要⾃⼰实现那么如何⾃⼰实现 那4个函数我们都要抱着这两个维度去学习。 1.要自己写的情况我们怎么写它的规范是什么要求是什么怎么样才能满足合理的需求怎么样写 构造函数大多数情况都需要自己写但是有些情况也可以让编译器自动生成。 下面我们来写一个栈 谁不需要自己写呢 我们之前是不是讲过一个用两个栈实现队列的题目啊 C语言的时候我们是不是写了个MyQueue的构造函数啊 如果用C实现是不是就是在MyQueue里面放两个栈啊 我们还需不需要写MyQueue的构造函数啊 不需要吧 我们刚刚是不是讲对于⾃定义类型成员变量要求调⽤这个成员变量的                                     默认构造函数初始化 C里面只要是对象就会调用构造调不到构造就会报错。 这两个栈是不是都初始化了 所以你也不能认为编译器默认生成的构造啥用没有少数场景下还                                     是有用的。 2.如果不写编译器默认生成的需求是什么 因为只有第一点我才能知道我要不要写。 2.赋值运算符重载 2.1 运算符重载 这个东西我们之前提过讲IO流的时候插入流和提取流是不是提过啊 当运算符被⽤于类类型的对象时C语⾔允许我们通过运算符重载的形式指定新的含义。C规定类类型对象使⽤运算符时必须转换成调⽤对应运算符重载若没有对应的运算符重载则会编译报错 内置类型都是支持各种运算符的运算符包含什么呢 加减乘除、比较大小等等等等 我们单说一个东西比较大小就拿我们的Date类来说 日期类能不能比较大小----可以啊而且比较日期大小在现实中还是很需要的是不是 举个简单的例子 有个程序比如你实现了一个日期类 那我得比较一下比如说这有一堆商品我想把这些商品以日期由远及近排过来 那日期是不是就得比较大小啊 ? 那我们日期比较大小怎么实现呢 我们内置类型的比较都是系统原生支持的 比如说我整型比较大小浮点数比较大小 但是你自定义的类型支不支持比较大小啊----不支持 自定义类型他要怎么去比较它的行为应该是我们自己定义的而不是由系统定义                     的 我们普通的运算符都是针对内置类型 自定义类型它要怎么做不知道 比如日期类你要怎么加 所以你得自己去写一个运算符 所以类类型对象使⽤运算符时必须转换成调⽤对应运算符重载。 若没有对应的运算符重载则会编译报错。 运算符重载是具有特殊名字的函数他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样它也具有其返回类型和参数列表以及函数体 也就是说运算符重载它会转换成一个函数 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数⼆元运算符有两个参数⼆元运算符的左侧运算对象传给第⼀个参数右侧运算对象传给第⼆个参数 那运算符重载的格式是什么呢 规定是这样的 一个operator的关键字运算符---构成函数名 他也有 参数 那它的参数是什么呢 参数要看这个操作符是一元的还是二元的 咱们c/c的运算符主要分为一元的和二元的 一元的就是一个参数 如 有的运算符一个有两重含义 如 这个是不是可以表示乘啊 但是它也可以表示解引用 所以它可以重载成乘也可以重载成解引用。这个我们后面迭代器的时候就会经常                  玩到。 那它的参数是什么呢看你需要什么。 二元就是两个参数 太多了加减乘除比较大小等等等等 比如说我们在这儿要比较日期类 我要传一个d1,一个d2 是不是就是两个参数啊 返回值 它的返回值是什么呢 看你这个运算符需要的是什么 比较大小它的返回值是不是一个布尔值啊 我们在这儿要支持比较相不相等怎么写 但是我们在这儿面临着一个问题 我们是不是没有访问Date类的成员变量的权限啊它们是private的在类外不能直接访问 不能访问这个私有怎么办 有这样的几种解决方案 1.最挫的方案 先把这个取消让它先跑起来 2.提供get函数Java贼喜欢用 你私有的我不能直接访问但我是不是可以通过get间接访问啊 注意提供get和公有不一样啊 如果你直接放成公有别人在类外面是直接能修改的。 但是你提供get的话不能修改。 get是获取到这个值嘛这儿是传值返回获取到的是它的拷贝。 上面那两种方式都不是太好还有一种方式让我们解决这个问题 既然类外面不行能不能放到类里面重载成成员函数呢------可以啊 因为成员函数也可以写各种函数嘛 但是当它重载成成员函数之后呢 要注意它的第一个对象默认就传给了隐含的this指针。 所以它已经有一个参数了那你是二元的 就变成明着写的是一个参数暗着写的是一个参数 所以这个地方就变成这样写了 那我们要调用这个函数的时候怎么调用呢 这样调 我们写的时候就这么写他会自动转换从成上面的 如果你是一元的就不写参数了 这块儿还有一个要注意的 比如说我在这儿定义了两个日期 传给二元运算符的时候谁是第一个谁是第二个呢 首先我们可以显式的调用这个函数 但是这样调用跟写个普通函数有啥区别呢 还可以怎么调用呢 还可以这样调用 你这样写它也会转换成上面调用的这个函数 函数体 函数体就是你界定它的行为的时候 如果⼀个重载运算符函数是成员函数则它的第⼀个运算对象默认传给隐式的this指针因此运算符重载作为成员函数时参数⽐运算对象少⼀个 运算符重载以后其优先级和结合性与对应的内置类型运算符保持⼀致 不能通过连接语法中没有的符号来创建新的操作符运算符⽐如operator 注意以下5个运算符不能重载。(选择题里面常考大家要记⼀下 .*是一个c语言没有的运算符 举个样例带大家看一下 这里有一个类 这个类假设我要去回调的方式去调用它的成员函数的指针 这个是普通函数的指针 普通的typedef都是这样的前面是类型后面是类型 但记住函数指针数组指针两个东西都比较特殊无论是定义变量还是                            typedef类型都要往里面放。 但是它要声明这个指针类型的时候成员函数还要加个这个 如果你想直接定义一个指针变量就这样定义 如果不是成员函数就是这样定义的 当然我们用了前面的typedef之后就可以这样写了 所以一般函数指针最好都用一下typedef 假设我现在想实现一个回调 回调我要把谁给给它呢 我要把func给给它 C规定普通函数的函数名就是函数指针 成员函数还得再加一个 那我们要回调这个函数指针怎么回调呢 以前是不是都是这样回调的 现在你这样回调不了 为什么回调不了 普通的全局函数是可以这样回调的 但是成员函数有一个特殊的地方 它有隐含的this指针 你要调用这个函数指针是不是得传隐含的this指针这个                                                           实参啊 那我能不能这样传一下呢 不行 为什么 this指针讲那一节的时候明确的讲过一个东西 我们的this指针在实参和形参的位置都不能显示 那怎么办 C规定回调成员函数的指针是这样回调的 这个时候就用到了一个运算符叫.* 也就是说 .*是在成员函数进行回调的时候用的 重载操作符运算符重载⾄少有⼀个类类型参数不能通过运算符重载改变内置类型对象的含义如 int operator(int x, int y) 内置类型直接用运算符就行了嘛重载不是有毛病嘛。 ⼀个类需要重载哪些运算符是看哪些运算符重载后有意义⽐如Date类重载operator-就有意 义但是重载operator就没有意义 我们做个简单的比方 两个日期减有没有意义有啊 一个日期减另外一个日期它是天数 日期和日期相加呢没有意义 没有意义就不会重载 重载运算符时有前置和后置运算符重载函数名都是operator⽆法很好的区分。 C规定后置重载时增加⼀个int形参跟前置构成函数重载⽅便区分。 重载和时需要重载为全局函数因为重载为成员函数this指针默认抢占了第⼀个形参位 置第⼀个形参位置是左侧运算对象调⽤时就变成了 对象cout不符合使⽤习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了第⼆个形参位置当类类型对象。 2.2 赋值运算符重载 赋值运算符重载是一个默认成员函数用于完成两个已经存在的对象之间的拷贝赋值。 这里要注意和拷贝构造区分拷贝构造用于一个对象拷贝初始化给另一个要创建的对象。 这是拷贝构造。 它先是一种初始化的行为再是拷贝。 它用于这样的场景已经存在的对象之间的拷贝赋值。 特点     首先他是一个运算符重载 并且规定它只能重载为成员函数不能重载为全局的。 赋值重载的参数建议写成const类类型引用否则传值传参会有拷贝。 只是建议这个地方你就算写成传值他也不会无穷递归了。 为什么 之前是拷贝构造去传值传参形成新的拷贝构造。 现在我是赋值重载调用那我传值传参d2传给d调用拷贝构造拷贝构造调用完             是不是回来了回来之后接着赋值啊不会无穷递归 这就是一个最简单的赋值重载 2. 有返回值 且建议写成当前类类型引⽤引⽤返回可以提⾼效率有返回值⽬的是为了⽀持连续赋值场景。 为什么他是有返回值的呢 因为赋值可能会存在这样的一些情况 C语言是不是支持这样的赋值啊 C语言是支持连续赋值的 这个赋值是这样走的赋值运算符的结合性是从右往左走。 1赋值给k赋值给k之后这个表达式是有一个返回值的这个返回值是k赋值表达式的 返回值是左操作数嗷。                k又作为这个地方的操作数再赋值给j。这个表达式再有个返回值是j                j再赋值给i 所以就实现了连续赋值 那我们重载自定义类型的运算符是不是也要实现这样的一个效果啊 你这个地方d1要赋值给d3,它的返回值应该是d3 d1传给了dd是d1的别名。 d3是传给谁的d3是传给this的this是d3的地址。 那怎么搞 这么搞 this在形参里面不允许显式写实参里不允许显式写但是类里面允许你显式写this 但是这样写还是不好 传值返回也会生成一个拷贝出了这个作用域这个this还在不在啊在啊                                 this指针的生命周期是和对象本身紧密相关的。 那你在这儿用传值返回是不是就白白生成了一个拷贝啊 所以这个地方用传引用返回。 这个时候我们回头来再看一个问题。 指针和引用虽然功能相似但是指针能不能帮你替代调引用啊 不能。 如果你非要替代它那你就这样写了。 你这个就得改成这个多挫啊。 所以引用在c里面和指针是一对相辅相成的兄弟各自有各自的价值。 3. 没有显式实现时编译器会自动生成一个默认赋值运算符重载默认赋值运算符重载行为跟默认拷贝构造函数类似对内置类型成员变量会完成值拷贝/浅拷贝一个字节一个字节的拷贝对自定义类型成员变量会调用他的赋值重载函数 我们这个地方不写它 这儿也能完成拷贝 因为Date类里面全是内置类型Int。 4. 像Date这样的类成员变量全是内置类型且没有指向什么资源编译器⾃动⽣成的赋值运算符重载就可以完成需要的拷⻉所以不需要我们显⽰实现赋值运算符重载。像Stack这样的类虽然也都是内置类型但是_a指向了资源编译器⾃动⽣成的赋值运算符重载完成的值拷⻉/浅拷⻉不符合我们的需求所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)。像MyQueue这样的类型内部主要是⾃定义类型Stack成员编译器⾃动⽣成的赋值运算符重载会调⽤Stack的赋值运算符重载也不需要我们显⽰实现MyQueue的赋值运算符重载。这⾥还有⼀个⼩技巧如果⼀个类显⽰实现了析构并释放资源那么他就需要显⽰写赋值运算符重载否则就不需要 和拷贝构造函数一样。 2.3 日期类实现 日期天数 日期加天数这里的逻辑是什么呢 进位逻辑和加法进位类似 做个简单的比方 我在这个地方10怎么加 这太简单了 50怎么加 加法的原则是满了就进位。 这是不是满了那是不是得进位啊而且可能不止进一次。 这个地方真正的麻烦事在于每个月的天数不一样。 我们先把这个逻辑走完7月是31天所以这儿应该减31往前进位。 基于这样的原因我们在这儿写一个函数出来----GetMonthDay 把这个数组定义成静态的是因为它要频繁调用那每次都创建这个数组就有                             点浪费了如果是所以我直接放到静态区是不是更好啊 剩下就很简单了 //d1 100 Date Date::operator(int day) {if (day 0){return *this - (-day);}_day day;while (_day GetMonthDay(_year, _month)){_day - GetMonthDay(_year, _month);_month;if (_month 13){_year;_month 1;}}return *this; } //d1 100 Date Date::operator(int day) const {Date tmp *this;tmp day;return tmp; } 日期-天数 加是一个进位逻辑 那减是一个借位逻辑 这种很简单 但这个时候就要借位 如果你减出来是一个负数或0那说明当前月的天数已经被减完了 比如这儿减出来应该是-37 那我要向6月去借借出来要加到对应的这个天数上面直到把它恢复成一个大于0的数。 //d1 - 100 Date Date::operator-(int day) {if (day 0){return *this (-day);}_day - day;while (_day 0){assert(_month 0 _month 13);//先--月份因为你要借的是上个月的天数--_month;if (_month 0){_year--;_month 12;}_day GetMonthDay(_year, _month);}return *this; } //d1 - 100 Date Date::operator-(int day) const {Date tmp *this;tmp - day;return tmp; } 我们可不可以-复用给-啊 可以 哪一种更好呢-复用-更好。它们之间真正的差异在-上面 -的效率本身是更低的 这儿有两次拷贝 如果你用-复用-呢 也一样两次拷贝无法规避。 所以对于减自己而言你用谁都是一样的 但是-不一样 -自己实现有没有拷贝--没有 但是-去复用-就吃亏了它拷贝了一次。 所以真正的差异点就在这个地方。 日期-日期 怎么减 思路1 一步一步借把它变成年月日都大的 天好说年也还行最麻烦的是月。 那我们换一种思路思路2 上面讲了两种方法都相对而言太麻烦了一些。 还有一种方法我们可以尽可能的去复用我们之前的逻辑 如果我们从可能的去复用我们之前的逻辑的角度来出发的话我们可以想象一下是不是我们可以直接取小的那个日期啊我们不是有比较大小嘛。取小了以后让小的那个日期不断。 中间经过了多少天n就是多少。 那我们的基本逻辑就差不多了。 基本逻辑差不多了之后现在可能还有一些小的问题。 不排除有人在这儿给你胡搞一个日期 所以我们在哪儿可以考虑一下把日期稍微检查一下呢我们可以在构造函数的时候把日           期稍微检查一下。 如果我想让用户自己来输入日期呢 scanf,printf能不能去输入输出自定义类型不能 用成员函数间接输入也可以但还是不方便。 所以这个地方有一个方法可以我们可以用流提取和流插入重载我们的输入输出。                         所以C为什么要搞IO流这一套 普通的printf和scanf它直接适应的是内置类型它们是不是要指定这个百分                          号符号啊对于类类型它没办法很好的适应。 所以祖师爷设计流插入和流提取这一套是为了干嘛呢 1.我支持内置类型 cout能直接使用内置类型的原因是什么呢它库里面帮你重载好了 它能自动识别类型是源自于函数重载。 那我们现在想自己支持它的流插入流提取怎么办我们是不是得自己重                                    载这个运算符啊 我们说自定义类型要去用运算符得自己重载重载了以后它遇到这个                                    运算符他会自动转换成函数去调用。 那我们来写一下 先来写个输出cout流插入 运行一下 哪儿出错了 参数不匹配 前面我们说了对于二元运算符它的左操作数对应第一个参数右操作数                             对应第二个参数。 这个地方能对的上吗 对不上 所以这个地方真要调用得这么写 ‘ 这个写法可以倒是可以但你有没有感觉这个写法就是倒反天罡                                                啊 我本来想表达的意思是这个日期类对象流向IO流 但现在怎么变成控制台流向日期类里面去了 这个时候怎么办除了可以写成成员函数运算符重载还可以写成全局                                     函数。 写成成员函数的目标是什么呢为了访问私有成员变量嘛方便嘛。 但是刚刚这儿写成成员函数的话访问成员变量倒是方便了但是                                           不对啊关键是我调整不了这两个参数。 所以我迫不得已就只能写成全局的。 我在这儿写成全局了我就可以把cout传给第一个参数作我们的左操作数 但这个时候是不是访问不了私有啊 那不就又回归到了我们之前的问题嘛 成员函数的方法不能用了 那就提供get函数吧 那现在教大家一种新方法这个新方法我们类和对象下才会讲但是我                                    们现在可以说一下。 在类里面可以访问私有在类外面不能。 但是有一种方式可以让类外面的函数访问类里面的东西加一个友元声                                      明友元函数 我跟你是陌生人我们俩不认识是不是啊 假如我和小明不认识。 那我能不能去小明家里面玩啊 不能是不是啊但我特别想去啊那怎么办呢 所以我要成为它的朋友 所以友元函数很简单就是我要去你家里面玩。那我怎么办呢我加一                                    个友元函数的声明。 这个我们类和对象下在详细讲我在这儿只是浅浅的带大家用一下。因                                  为它很简单就是增加一个关键字friend。 我是你的朋友一般友元声明都喜欢加在比较前面的位置 它不能实现成成员函数它只能实现成全局的。 实现成全局的它就涉及到访问私有的问题。 但是它并不是必须设计访问私有嗷如果需要访问私有那这个时候才                                    需要设计成友元。 流插入和流提取并不是说必须设计成友元嗷。 我们后面讲到string类那个地方就可以不设计成友元。 比如我不需要访问你的私有我就不需要友元。 我需要访问你的私有我才在这个地方设计成你的友元。 它呢还有一些没有解决的问题如果我要连续的在这个地方输呢                        ​​​​​​​                        ​​​​​​​ 是不是不行啊 原因是什么呢 这个要看运算符的结合性。 我之前的赋值呢是从右往左结合它呢是从左往右结合。 从左往右结合就是先走它 走它是不是转换成了一个函数调用啊 cout传给outd1传给d 调用完了这个表达式是不是该有个返回值啊 返回值一样还是左操作数左操作数就是cout。 然后d2再流插入到cout。它是这样的逻辑走的嗷 out就是cout啊。所以我们返回out即可。 OK没问题了。 这也支持了 再来写一下流提取输入 isream,ostream类型对象必须用引用嗷不能传值 传值它会报错因为它不只是拷贝构造 流提取这里就不能加const了 流提取在这个地方用cout会不会造成一些问题呢 不会 之前说过cin和cout具有绑定关系 c第一它兼容C语言所以它和C语言的缓冲区保持同步。也就意味着比如你上面用的printf下面用的coutprintf没有带刷新标志因为它们都在缓冲区嘛printf有缓冲区cout有缓冲区。 我兼容C语言就是我要进行刷新的时候我会把前面的printf的缓冲区也进行刷新。 比如 printf没有进行刷新cout进行了刷新那cout刷新出去的时候它会先把printf缓冲区的内容刷新出去。 所以它兼容C语言两个不会乱。 但是这个东西会牺牲效率所以在IO里面如果要求效率比较高。 也就是说我C cout刷新的时候不去刷新C语言的printf。 这个是跟C语言的同步关掉 这个是跟其他的流全部绑定关系都关掉 其次cin和cout也是绑在一起的 cin和cout为什么是绑在一起的呢 就像我现在写的这个 cout是不是也是带缓冲区的假设这一句到它的缓冲区里面之后 大家知道刷新缓冲区是有一些要求的比如遇到换行符或者主动进行刷新或者是程序结束等等等等这些才会进行刷新。 那这个进去了以后它不会到控制台它只会在它的缓冲区。 所以当进行cinin就是cin嘛流提取的时候它会去把cout主动刷新出去。 默认它和cout是绑在一起的。 默认情况下,cin是同步stdin它跟C语言是同步的。 其次它默认是绑定到output也就是cout的。 意思就是说cin刷新的时候会刷新cout的缓冲区 你在这儿走这个东西到缓冲区了 我在这儿提取的时候。这个东西没有到缓冲区 因为它们各自有各自的缓冲区嘛不用担心。 在你进行流提取之前它会把cout的buffer刷新一下。 所以你在进行流提取之前这个一定出去了。 那我们现在就可以这么写 再来简单算一个 如果我输入一个这个日期呢 咋回事程序咋出不来了 6月31是不是一个非法日期啊 所以我们这个程序就应该再改进一下 这是不是就可以了 //流输入 istream operator(istream in, Date d) {while (1){cout 请依次输入年月日:;in d._year d._month d._day;if (!d.CheckDate()){cout 输入日期非法;d.Print();cout 请重新输入 endl;}else{break;}}return in; } 介绍一下运算符重载和函数重载 虽然他们俩都用了重载这个词但是它们俩没有关系 函数重载指的是函数名相同参数不同 运算符重载指的是重新定义这个运算符的行为 两个运算符重载的函数又可以构成函数重载因为它们的函数名相同参数不同。 实现一下比较运算符 两个日期比较大小怎么比呢 那小于等于怎么写呢 有没有必要cv呢没有 教大家一种通用的方法这种方法不仅仅对日期类有用针对所有的类都有用。 写了一个下一个你就去写个。 剩下的逻辑统统复用 不就是取个反吗 不就是取反吗 !不就是取反吗? 你必须得先写两个出来先写和也可以。 再来实现一下 这两个怎么区分啊 二元是规定了左边是第一个参数右边是第二个参数。 这俩都是一元运算符。这两个函数都不能同时存在。 所以祖师爷在这个地方是怎么考量的呢 这样考量的。这两个人谁用的多呢 是不是前置按理来说用的多啊前置的拷贝会少一些前置是返回以后的值可以传引用返回。 后置去做出改变增加一个参数。这个参数你加不加形参名都行编译器它不接收 编译器在这个地方也做了特殊处理。可以这么理解 如果你是后置他会转换成去这样调用 这个地方的参数只是为了区分所以可以不接收 实现 声明 ​​​​​​​        ​​​​​​​         定义 如果你是前置在这个地方就是正常的 实现 声明 定义 -- 填个坑 有人会写这样的代码 贼坑 说明一个问题 你在实现或-的时候除了要考虑这个day是正数是不是还要考虑这个day是负数啊 -也一样 Date.h #pragma once #includeiostream using namespace std; #includeassert.hclass Date {//友元函数声明friend ostream operator(ostream out, Date d);friend istream operator(istream in, Date d); public:Date(int year 1990, int month 1, int day 1);void Print() const;// ?inlineint GetMonthDay(int year, int month){int MonthDayArry[] { -1, 31,28,31,30,31,30,31,31,30,31,30,31 };if (month 2 ((year % 4 0) (year % 100 ! 0) || (year % 400 0))){return 29;}return MonthDayArry[month];}//赋值Date operator(const Date d);bool CheckDate();bool operator(const Date d) const;bool operator(const Date d) const;bool operator(const Date d) const;bool operator(const Date d) const;bool operator(const Date d) const;bool operator!(const Date d) const;Date operator(int day) const;Date operator(int day);Date operator-(int day) const;Date operator-(int day);//d1Date operator();//d1Date operator(int);//--d1Date operator--();//d1--Date operator--(int);//d1 - d2int operator-(const Date d);//流插入//ostream operator(const Date d);int GetYear();int GetMonth();int GetDay();Date* operator(){return nullptr;}const Date* operator() const{return nullptr;} private:int _year;int _month;int _day; };//流输出 ostream operator(ostream out, Date d); //流输入 istream operator(istream in, Date d); Date.cpp #define _CRT_SECURE_NO_WARNINGS #includeDate.h bool Date::CheckDate() {if (_month 1 || _month 12|| _day GetMonthDay(_year, _month)){return false;}else{return true;} } //构造函数 Date::Date(int year, int month, int day) {_year year;_month month;_day day;if (!CheckDate()){cout 非法日期 endl;Print();} }void Date::Print() const {cout _year / _month / _day endl; } //赋值 Date Date::operator(const Date d) {_year d._year;_month d._month;_day d._day;return *this; }//d1 d2 bool Date::operator(const Date d) const {if (_year d._year){return true;}else if (_year d._year){//比月份if (_month d._month){return true;}else if (_month d._month){return _day d._day;}}else{return false;} }//d1 d2 bool Date::operator(const Date d) const {return _year d._year _month d._month _day d._day; } //d1 d2 bool Date::operator(const Date d) const {return *this d || *this d; } //d1 d2 bool Date::operator(const Date d) const {return !(*this d); } //d1 d2 bool Date::operator(const Date d) const {return !(*this d); }//d1 ! d2 bool Date::operator!(const Date d) const {return !(*this d); } //d1 100 Date Date::operator(int day) {if (day 0){return *this - (-day);}_day day;while (_day GetMonthDay(_year, _month)){_day - GetMonthDay(_year, _month);_month;if (_month 13){_year;_month 1;}}return *this; }//d1 - 100 Date Date::operator-(int day) {if (day 0){return *this (-day);}_day - day;while (_day 0){assert(_month 0 _month 13);//先--月份因为你要借的是上个月的天数--_month;if (_month 0){_year--;_month 12;}_day GetMonthDay(_year, _month);}return *this; }//d1 100 Date Date::operator(int day) const {Date tmp *this;tmp day;return tmp; }//d1 - 100 Date Date::operator-(int day) const {Date tmp *this;tmp - day;return tmp; }//d1 Date Date::operator() {_day;if (_day GetMonthDay(_year, _month)){_month;_day 1;}if (_month 13){_year;_month 1;}return *this; } //d1 Date Date::operator(int) {Date tmp *this;*this;return tmp; } //--d1 Date Date::operator--() {--_day;if (_day 0){assert(_month 0 _month 13);--_month;if (_month 0){--_year;_month 12;}_day GetMonthDay(_year, _month);}return *this; } //d1-- Date Date::operator--(int) {Date tmp *this;--*this;return tmp; }//d1 - d2 int Date::operator-(const Date d) {//找小的那个日期//假设法int flag 1;Date min d;Date max *this;if (*this d){min *this;max d;flag -1;}int day 0;while (min ! max){min;day;}return day * flag; }//int Date::GetYear() //{ // return _year; //} //int Date::GetMonth() //{ // return _month; //} //int Date::GetDay() //{ // return _day; //}//流输出 ostream operator(ostream out, Date d) {out d._year 年 d._month 月 d._day 日 endl;return out; } //流输入 istream operator(istream in, Date d) {while (1){cout 请依次输入年月日:;in d._year d._month d._day;if (!d.CheckDate()){cout 输入日期非法;d.Print();cout 请重新输入 endl;}else{break;}}return in; } 3. 取地址运算符重载 那取地址运算符重载这个地方要看呢这个地方就看一下什么东西呢 3.1 const成员函数 首先得来看一下我们这个地方这里的成员函数。成员函数这里会提出一个const成员函数的概念。 const成员函数是说用const去修饰成员函数 怎么修饰呢放到成员函数参数列表的后面。 为什么要放到这儿呢 我们的对象可能会有const对象。const对象去调用函数的时候是有一定程度的限制的。 比如用这个const对象去调用我们之前的print函数。 因为这里涉及到权限的问题涉及到权限放大和缩小的问题。 还记不记得之前和大家讲过说权限可以平移可以缩小但不能放大。 我们的对象有普通对象也可能会有const对象。当const对象去调用普通成员函数的时候会有导致权限放大。 那权限被放大了你const对象不允许被修改啊。 这个地方是不是要取d1的地址传过去啊d1地址的类型是什么---const Date* 对不对 因为它取了它的地址的话这个const就是事实对象本身嘛那这个指针就是指向内容不能修改。 那默认情况下这个地方的指针是什么呢 这个地方的指针我先讲简单一点啊讲复杂了大家容易绕进去。 这个地方的指针是Date* 那大家看这是不是一个经典的权限放大 这样写的意思是这个年月日是可以修改的 我自己指向的内容都不能修改你还能修改我自己的内容那不扯淡呢嘛。 这个地方也有一个const。 但是这个地方的const是修饰this指针本身,不是指针指向的内容。 所以这个位置必须得想办法这样变才可以。 在前面想办法去加个const。 那这个地方怎么去改变this指针的类型呢是不是没办法加啊因为this指针我们当时讲的时候是这样讲的this指针在类里面可以用在函数里面可以用但是在实参和形参的位置我们都不能写我们不能碰它是不是啊 所以祖师爷迫不得已在后面就出了一个偏方来治这个病。 这个偏方就是把const加在这个位置 现在还能不能修改 不能 因为加了const以后它本质上就变成这个了 严格来说是这个 第二个问题 如果这个对象是非const能不能调用 可以 因为权限虽然不能放大但是可以缩小 所以成员函数加上const有没有好处啊有极大的好处因为普通对象和const对象是不是都可以调用啊 那我们能不能简单粗暴的把所有的函数都加上const呢 不能行因为有些函数比如我们的构造函数本身就要修改成员变量 构造函数你能加const吗不能 你要让我初始化你都把我加const我咋初始化 所以以后的原则是不修改成员变量的建议把const加上。 3.2取地址运算符重载 取地址运算符重载之前为什么要先讲const成员函数呢因为const成员函数本身也需要讲一讲。 放到这儿的原因是因为取地址运算符的重载有两个。 普通的取地址重载 const取地址重载 它里面其实就是返回this哦。 返回对象的地址嘛this不就是对象的地址嘛。 那它为什么要重载两个呢我重载一个const版本不就好了吗 是这样的一般它们俩都不需要重载系统库直接就支持了 所以我们在这个地方写这个的时候是不需要重载的其他运算符都需要重载 取地址运算符不需要重载 因为它是默认成员函数。实际当中默认生成的就够用了我们就不用管 除非我想使坏 假如我不想让别人取到我日期类的对象地址 这还不是最狠的最狠的是我故意误导别人。我给别人一个随机地址。 那为什么他们俩两个都要写呢 如果你是普通对象你返回的是Date* const对象返回的是const Date*如果你没写上面那个普通对象也可以调用它但是返回const Date*就不合理。 所以有时候当一个函数的两个版本同时存在的时候是可以的。他俩构成函数重载因为他俩返回类型不同并且编译器去调用的时候会去调用最匹配的。
http://www.w-s-a.com/news/756676/

相关文章:

  • 公司注册网站开发的行业表述南宁在百度上建网站
  • 创建企业网站国内网站用django做的
  • 云主机网站的空间在哪制作微网站的平台
  • 长沙做网站 青创互联wordpress4.4.1
  • 宜昌哪里有专业做网站的网站开发做什么的
  • 3小说网站开发东莞网站公司哪家好
  • 做网站安全联盟解ps网站设计概述
  • 聊城公司做网站wordpress连接域名
  • 宣传网站建设的意义台州行app官网下载
  • 温州 网站优化网站开发公司前置审批
  • 网站开发具体的工作内容网站下载app免费
  • seo网站建设时文章频率昆山网站建设ikelv
  • 中天建设中瑞物资网站优化建立生育支持政策体系
  • 网站页面的宽度大网站怎样选域名
  • icp网站备案流程wordpress post 405
  • 网站怎样上传到空间重庆有多少网站
  • 用模板建商城购物网站嘉定专业网站建设
  • 网站开发与应用 论文dede手机医院网站模板
  • 织梦 网站栏目管理 很慢自学网页设计难吗
  • 茶文化建设网站的意义平顶山网站建设服务公司
  • 建设网站详细流程南京宣传片制作公司
  • 合肥网站排名什么网站做电气自动化兼职
  • 如何用api做网站交通建设门户网站
  • 阳西住房和城乡规划建设局网站长沙网站seo技巧
  • 长沙知名网站推广手机画设计图软件
  • 顺德公司做网站自己有网站怎么优化
  • 南京网站开发南京乐识专业外贸流程知乎
  • 盐田区住房和建设局网站分类网站有哪些
  • 建一个团购网站WordPress文章字号设置
  • 做漂亮的网站东营网站seo