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

dedecms微电影网站模板做网站公司促销海报

dedecms微电影网站模板,做网站公司促销海报,wordpress提交360,宁波seo基础优化平台第一章#xff1a;类的6个默认成员函数 如果一个类中什么成员都没有#xff0c;简称为空类。 空类中真的什么都没有吗#xff1f;并不是#xff0c;任何类在什么都不写时#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数#xff1a;用户没有显式实现类的6个默认成员函数 如果一个类中什么成员都没有简称为空类。 空类中真的什么都没有吗并不是任何类在什么都不写时编译器会自动生成以下6个默认成员函数。 默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数。 第二章构造函数  2.1 概念 class Date { public:void Init(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 d1;d1.Init(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0; }对于Date类可以通过 Init 公有方法给对象设置日期但如果每次创建对象时都调用该方法初始化用完后还要销毁。可能会忘记这些步骤。所以引出构造函数。 构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用以保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次。 2.2 特性 构造函数是特殊的成员函数需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并不是开空间创建对象而是初始化对象。 其特征如下 1. 函数名与类名相同。 2. 无返回值。不需要写void 3. 对象实例化时编译器自动调用对应的构造函数。 class Stack { public://void Init() { // a nullptr;// top capacity 0;//}//如果按下方这种方式初始化不能完全满足所有需求。//如果要向栈里插入1000个元素就需要多次扩容。如果是异地扩容开销很大因为需要拷贝数据//Stack() { //构造函数// a nullptr;// top capacity 0;//}//优化版本Stack(size_t n 4) {// n 为 01.避免无效内存分配//2.防止错误状态初始化 a 为 nullptr 和将 top、capacity 设置为 0//确保对象处于有效的初始状态即使不分配内存也不会导致未定义的行为或内存访问错误。if (n 0) {a nullptr;top capacity 0;}else {a (int*)malloc(sizeof(int) * n);if (a nullptr) {perror(malloc a fail);exit(-1);}top 0;capacity n;}}void Push(int x) {if (top capacity) { int newcapacity capacity 0 ? 4 : capacity * 2;int* tmp (int*)realloc(a, sizeof(int) * newcapacity);if (!tmp) {perror(realloc int* tmp fail);exit(-1);}if (tmp a)cout capacity 原地扩容 endl;//打印elsecout capacity 异地扩容 endl;//打印a tmp;capacity newcapacity;}a[top] x;}int Top() {assert(top 0);return a[top - 1];}void Pop() {assert(top 0);top--;}bool Empty() {return top 0;}void Destroy() {free(a);a nullptr;top capacity 0;}private: int* a;int top;int capacity; };int main() {//栈1Stack st1;st1.Push(1);st1.Push(2);st1.Push(3);st1.Push(4);while (!st1.Empty()) {cout st1.Top() ;st1.Pop();}cout endl;st1.Destroy();//栈2//Stack st2;//如果不设计全缺省的构造函数就需要多次扩容Stack st2(1000);for (size_t i 0; i 1000; i) {st2.Push(i);}while (!st2.Empty()) {cout st2.Top() ;st2.Pop();}cout endl;st2.Destroy(); }4. 构造函数可以重载。可以写多个构造函数提供多种初始化方式 class Date { public:// 1.无参构造函数Date() {_year 1;_month 1;_day 1;}// 2.带参构造函数Date(int year, int month, int day) {_year year;_month month;_day day;}//3.合并上方两种写法全缺省参数。并且更灵活//1和3可以同时存在构成函数重载但是调用存在问题。如果不传参编译器不知道调用的是1还是3Date(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, 8, 12); // 调用带参的构造函数d2.Print();Date d3(2024, 8);//全缺省演示d3.Print();Date d4(2024);//全缺省演示d4.Print();//Date d3();//错误演示1。如果通过无参构造函数创建对象时对象后面不用跟括号否则就成了函数声明//Date d5();//错误演示2。虽然是全缺省构造函数但不传参对象后面不用跟括号否则就成了函数声明//d5.Print();return 0; } 5. 如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。 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;return 0; } 6. 关于编译器生成的默认成员函数会产生疑惑不实现构造函数的情况下编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用d对象调用了编译器生成的默认构造函数但是d对象_year/_month/_day依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用 解答C把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型如int/char...自定义类型就是我们使用class/struct/union等自己定义的类型看看下面的程序就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认构造函数。 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;//Time()return 0; } 注意C11 中针对内置类型成员不初始化的缺陷又打了补丁即 内置类型成员变量在类中声明时可以给默认值。 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 1970;int _month 1;int _day 1;// 自定义类型Time _t; };int main() {Date d;return 0; } 7. 无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。注意无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数都可以认为是默认构造函数。 不传参就可以调用的就是默认构造函数三个只能存在一个否则调用存在歧义 编译器生成的默认构造函数的特点 1.如果不写才会生成否则写了任意一个构造函数就不会生成了 class Date { public:void Print() {cout _year / _month / _day endl;} private:int _year;int _month;int _day; };int main() {//如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数//一旦用户显式定义编译器将不再生成。//示例1没有显示定义编译器自动生成无参构造函数Date d1;//这里生成了构造函数也可以调用。但没初始化。d1.Print();//打印时都是随机值。因为内置类型成员不处理初始化。C11声明支持给缺省值return 0; }2.内置类型的成员(即int、char之类关键字定义)不会处理C11声明支持给缺省值 //示例1 class Date { public:void Print() {cout _year / _month / _day endl;} private://内置类型的成员(即int、char之类关键字定义)不会处理C11声明支持给缺省值//但注意不要重复初始化如果自己定义了构造函数且初始化就不要在内置类型声明时给缺省值int _year 1;//声明给的缺省值int _month 1;int _day 1; };int main() {Date d1;// 1/1/1d1.Print();return 0; }//示例2 class Date { public:Date() {//这里没有初始化year所以year就是内置类型声明时的1_month 2;_day 2;}void Print() {cout _year / _month / _day endl;} private:int _year 1;int _month;int _day; };int main() {Date d1;// 1/2/2d1.Print();return 0; } 3.自定义类型的成员(即类、结构体)才会处理会去调用这个成员的默认构造函数 class Stack { public:Stack(size_t n 4) {cout Stack(size_t n 4) endl;//为了验证MyQueue这个类编译器自动生成的构造函数if (n 0) {a nullptr;top capacity 0;}else {a (int*)malloc(sizeof(int) * n);if (a nullptr) {perror(malloc a fail);exit(-1);}top 0;capacity n;}}void Push(int x) {if (top capacity) {int newcapacity capacity 0 ? 4 : capacity * 2;int* tmp (int*)realloc(a, sizeof(int) * newcapacity);if (!tmp) {perror(realloc int* tmp fail);exit(-1);}if (tmp a)cout capacity 原地扩容 endl;//打印elsecout capacity 异地扩容 endl;//打印a tmp;capacity newcapacity;}a[top] x;}int Top() {assert(top 0);return a[top - 1];}void Pop() {assert(top 0);top--;}bool Empty() {return top 0;}void Destroy() {free(a);a nullptr;top capacity 0;}private:int* a;int top;int capacity; };//两个栈实现一个队列 class MyQueue { private:Stack _pushst;Stack _popst; };int main() {//示例3自定义类型的成员(即类、结构体)才会处理会去调用这个成员的构造函数MyQueue mq;//mq不需要写构造函数。编译器会生成默认构造函数该构造函数会使用Stack的构造函数return 0; }总结一般情况下都需要我们自己写构造函数决定初始化方式。 第三章析构函数 3.1 概念 析构函数与构造函数功能相反析构函数不是完成对对象本身的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作。 3.2 特性 析构函数是特殊的成员函数其特征如下 析构函数名是在类名前加上字符 ~。无参数无返回值类型。一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。注意析构函数不能重载对象生命周期结束时C编译系统系统自动调用析构函数。编译器生成的默认析构函数对自定类型成员调用它的析构函数。如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如Date类有资源申请时一定要写否则会造成资源泄漏比如Stack类。 示例1 class Date { public:Date(int year 1, int month 1, int day 1) {cout Date(int year 1, int month 1, int day 1) endl;_year year;_month month;_day day;}void Print() {cout _year / _month / _day endl;}~Date() {//这里只是为了证明自动调用了析构函数。//这个类并没有要清理的资源cout ~Date() endl;} private:int _year;int _month;int _day; };int main() {Date d;return 0; }示例2 class Stack { public:Stack(size_t n 4) {//cout Stack(size_t n 4) endl;//为了验证MyQueue这个类编译器自动生成的构造函数if (n 0) {a nullptr;top capacity 0;}else {a (int*)malloc(sizeof(int) * n);if (a nullptr) {perror(malloc a fail);exit(-1);}top 0;capacity n;}}void Push(int x) {if (top capacity) {int newcapacity capacity 0 ? 4 : capacity * 2;int* tmp (int*)realloc(a, sizeof(int) * newcapacity);if (!tmp) {perror(realloc int* tmp fail);exit(-1);}if (tmp a)cout capacity 原地扩容 endl;//打印elsecout capacity 异地扩容 endl;//打印a tmp;capacity newcapacity;}a[top] x;}int Top() {assert(top 0);return a[top - 1];}void Pop() {assert(top 0);top--;}bool Empty() {return top 0;}//void Destroy() {// free(a);// a nullptr;// top capacity 0;//}~Stack() {//cout ~Stack() endl;//为了验证自动调用析构函数free(a);a nullptr;top capacity 0;}private:int* a;int top;int capacity; };int main() {//后定义先析构Stack st1;Stack st2;因为在栈帧里面后进先出。所以调用的第一个析构是st2return 0; } 有了构造函数和析构函数可以重新对括号匹配问题进行改写 1. 创建并初始化栈 2. 数组指针遇到左括号入栈 3. 数组指针遇到右括号份2种情况     a. 栈为空返回假。(说明之前没有遇到左括号)     b. 栈不为空获取栈顶元素与数组指针指向元素比较是否匹配。(注意匹配条件找为假的情况) 重复上述步骤直到遍历完数组。 4.最后判断栈是否为空。空为真非空为假。   C语言版本 bool isValid(char* s) {//1.创建并初始化栈ST st;STInit(st);//2.遍历字符串while (*s) {if (*s ( || *s [ || *s {) //1.如果是左括号就入栈STPush(st, *s);else { //2.不是左括号就出栈if (STEmpty(st)) { //2.1 出栈前先看栈是否为空为空说明没有左括号直接返回假STDestroy(st);return false;}int top STTop(st); //获取栈顶元素STPop(st);//2.2 如果栈顶元素的左括号和字符串指针指向的右括号不匹配则返回假if ((top ( *s ! )) || (top [ *s ! ]) || (top { *s ! }))return false; }s;}bool ret STEmpty(st);//遍历完后还需看栈是否为空为空说明还有单独的左括号不匹配STDestroy(st);return ret; } C版本 bool isValid(const char* s) {Stack st;// 创建栈 // 1.左括号入栈// 2.出栈元素与右括号匹配while (*s) { // 遍历数组 if (*s ( || *s [ || *s {) // 左括号入栈st.Push(*s);else { // 出栈元素与右括号匹配 // 在出栈之前要判断栈是否为空如果为空说明没有左括号直接返回假if (st.Empty())return false;char top st.Top(); // 获取栈顶元素st.Pop(); // 将栈顶元素出栈//if ((*s ) top () || (*s ] top [) || (*s } top {))//这里不能用上方条件判断正确后直接返回true因为最后一个元素为左括号时也满足但不正确 //下方如果栈顶元素与数组指针指向元素不匹配返回假if ((*s ) top ! () || (*s ] top ! [) || (*s } top ! {))return false;}s;}//这里判断栈里是否还有元素// 如果有说明有一个单独的左括号不匹配// 如果没有说明都匹配完成bool ret st.Empty();return ret; } 第四章拷贝构造函数 4.1 概念 拷贝构造函数只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。 4.2 特征 拷贝构造函数也是特殊的成员函数其特征如下 1. 拷贝构造函数是构造函数的一个重载形式。 2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷递归调用。 class Date { public:Date(int year 1, int month 1, int day 1) {_year year;_month month;_day day;}//1.传值调用//当你调用 Date d2(d1); 时d1 是实参d2 是要被创建的新对象。//为了将 d1 传递给拷贝构造函数编译器需要先创建一个 Date 对象作为 d 形参的临时拷贝。//2.创建临时拷贝//在这个过程中编译器会创建一个新的 Date 对象来作为 d 的值。//这个临时对象的创建是通过调用拷贝构造函数实现的因为它需要拷贝 d1 对象。//3.触发拷贝构造函数//为了创建这个临时拷贝对象编译器会调用拷贝构造函数 Date(Date d)来初始化这个新的 Date 对象。//4.递归调用//由于拷贝构造函数的参数类型是 Date 对象而不是引用//这个新的 Date 对象即临时拷贝也需要使用拷贝构造函数来完成初始化。//这导致了无限递归因为拷贝构造函数会被不断调用来初始化新的临时拷贝对象。//因为是传值调用且d1是实参在传参时形参是实参的一份临时拷贝。//这里需要拷贝的是类的对象而实现这个类对象拷贝的函数是拷贝构造函数。//根据上述所说传参时会发生实参的临时拷贝也就是调用拷贝构造函数去拷贝d1。//因此造成了无限递归如果参数是按值传递的Date d那么就会在调用拷贝构造函数时创建一个新的对象这可能会导致无限递归。//Date(Date d) { //错误示例// _year d._year;// _month d._month;// _day d._day;//}//Date d2(d1);//d是d1的别名d1不能修改Date(const Date d) { //正确版本//推荐加上const 防止写反d._year _year;_year d._year;_month d._month;_day d._day;}void Print() {cout _year / _month / _day endl;} private:int _year;int _month;int _day; };3. 若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。  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;// 用已经存在的d1拷贝构造d2此处会调用Date类的拷贝构造函数// 但Date类并没有显式定义拷贝构造函数则编译器会给Date类生成一个默认的拷贝构造函数Date d2(d1);return 0; } 注意在编译器生成的默认拷贝构造函数中内置类型是按照字节方式直接拷贝的而自定义类型是调用其拷贝构造函数完成拷贝的。 4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了还需要自己显式实现吗当然像日期类这样的类是没必要的。那么下面的类呢 // 这里会发现下面的程序会崩溃掉这里就需要后面讲的深拷贝去解决。 typedef int DataType; class Stack { public:Stack(size_t capacity 10) {_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array) {perror(malloc申请空间失败);return;}_size 0;_capacity capacity;}void Push(const DataType data) {// CheckCapacity();_array[_size] data;_size;}~Stack() {if (_array) {free(_array);_array nullptr;_capacity 0;_size 0;}} private:DataType* _array;size_t _size;size_t _capacity; };int main() {Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0; } 注意类中如果没有涉及资源申请时拷贝构造函数是否写都可以一旦涉及到资源申请时则拷贝构造函数是一定要写的否则就是浅拷贝。  栈类的正确版本 typedef int DataType; class Stack { public:Stack(size_t capacity 4) {cout Stack(size_t capacity 4) endl;if (capacity 0) {_array nullptr;_size _capacity 0;}else {_array (int*)malloc(sizeof(int) * capacity);if (_array nullptr) {perror(malloc a fail);exit(-1);}_size 0;_capacity capacity;}}Stack(const Stack s) {cout Stack(Stack s) endl;_array (DataType*)malloc(sizeof(DataType) * s._capacity);//开辟新对象_array的空间if (_array nullptr) {perror(malloc a fail);exit(-1);}//将要拷贝对象中的数据拷贝过来memcpy(_array, s._array, sizeof(DataType) * s._size);_size s._size;_capacity s._capacity;}void Push(DataType data) {// CheckCapacity();_array[_size] data;_size;}~Stack() {cout ~Stack() endl;free(_array);_array nullptr;_size _capacity 0;} private:// 内置类型DataType* _array;int _capacity;int _size; }; 5. 拷贝构造函数典型调用场景 使用已存在对象创建新对象函数参数类型为类类型对象函数返回值类型为类类型对象 为了提高程序效率一般对象传参时尽量使用引用类型返回时根据实际场景能用引用尽量使用引用。 第五章赋值运算符重载 5.1 运算符重载 C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。 函数名字为关键字operator后面接需要重载的运算符符号。 函数原型返回值类型 operator操作符(参数列表) 注意 不能通过连接其他符号来创建新的操作符比如operator 重载操作符必须有一个类类型参数用于内置类型的运算符其含义不能改变例如内置的整型不 能改变其含义作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this.*   ::   sizeof   ?:   .   注意以上5个运算符不能重载。这个经常在笔试选择题中出现。 全局版 class Date { public: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; };//bool riqixiaoyu(const Date x1, const Date x2) //bool Compare1(const Date x1, const Date x2) //为了避免出现上面函数名不明确且提高代码可读性cout d1 d2 endl;所以引出了运算符重载 //编译器并不知道自定义类型怎么比较所以需要自己实现全局版 bool operator(const Date x1, const Date x2) { //传值传参要去调用拷贝构造还要开辟空间所以用传引用传参if (x1._year x2._year)return true;else if (x1._year x2._year x1._month x2._month)return true;else if (x1._year x2._year x1._month x2._month x1._day x2._day)return true;elsereturn false; }int main() {Date d1(2024, 8, 19);Date d2(2023, 7, 18);//d1 d2;//不能用这个运算符比较日期大小。所以需要一个比较函数cout (d1 d2) endl;//这里比较需要加括号因为流运算符优先级更高cout (operator(d1, d2)) endl;//显示调用。operator加就是函数名return 0; } 待解决问题成员变量不在是私有 将比较函数改为成员函数 class Date { public:Date(int year 1, int month 1, int day 1) {_year year;_month month;_day day;}void Print() {cout _year / _month / _day endl;}//bool operator(const Date x1, const Date x2) { //错误版本//成员函数的签名中运算符重载函数只有一个参数因为左侧的对象隐式地作为 this 被传递。//这里的 operator 函数有两个参数这在成员函数中是不允许的。// bool operator(Date* this, const Date x2)// 这里需要注意的是左操作数是this指向调用函数的对象bool operator(const Date x2) {if (_year x2._year)return true;else if (_year x2._year _month x2._month)return true;else if (_year x2._year _month x2._month _day x2._day)return true;elsereturn false;}private:int _year;int _month;int _day; };int main() {Date d1(2024, 8, 19);Date d2(2023, 7, 18);cout (d1 d2) endl;cout (d1.operator(d2)) endl;return 0; }5.2 赋值运算符重载 1. 赋值运算符重载格式 参数类型const T传递引用可以提高传参效率返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值检测是否自己给自己赋值返回*this 要复合连续赋值的含义 class Date { public:Date(int year 1900, int month 1, int day 1) {_year year;_month month;_day day;}Date(const Date d) {_year d._year;_month d._month;_day d._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; }; 2. 赋值运算符只能重载成类的成员函数不能重载成全局函数 原因赋值运算符如果不显式实现编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载就和编译器在类中生成的默认赋值运算符重载冲突了故赋值运算符重载只能是类的成员函数 3. 用户没有显式实现时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝。 内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。 编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了所以像日期类这样的类是没必要的。 注意如果类中未涉及到资源管理赋值运算符是否实现都可以一旦涉及到资源管理则必须要实现。 5.3 前置和后置重载 //前置和后置不仅是运算符重载还构成函数重载class Date { public:Date(int year 1900, int month 1, int day 1) {_year year;_month month;_day day;}// 前置返回1之后的结果// 注意this指向的对象函数结束后不会销毁故以引用方式返回提高效率Date operator() {_day 1;return *this;}// 后置// 前置和后置都是一元运算符为了让前置与后置形成能正确重载// C规定后置重载时多增加一个int类型的参数但调用函数时该参数不用传递编译器自动传递// 注意后置是先使用后1因此需要返回1之前的旧值故需在实现时需要先将this保存一份然后给this 1// 而temp是临时对象因此只能以值的方式返回不能返回引用Date operator(int) {Date temp(*this);_day 1;return temp;} private:int _year;int _month;int _day; };int main() {Date d;Date d1(2022, 1, 13);d d1;// d: 2022,1,13   d1:2022,1,14d d1;// d: 2022,1,15   d1:2022,1,15return 0; } 第六章日期类的实现 Date.h #pragma once #include iostream #include assert.h using namespace std;class Date {//friend void operator(ostream out, const Date d);//通过友元声明让类外部函数可以访问内部私有成员变量friend ostream operator(ostream out, const Date d);//支持连续流插入friend istream operator(istream in, Date d);public://声明在类的声明部分提供默认参数是为了让编译器和使用者了解函数的接口和参数的默认值。//声明是在头文件中进行的目的是让其他代码知道如何调用该函数。//缺省参数声明给定义不给。因为.cpp文件包含的是.h的头文件//声明给了缺省参数编译才能通过。链接时才找函数的地址int GetMonthDay(int year, int month);Date(int year 1, int month 1, int day 1);//下方两个函数构成函数重载可以同时存在。//但这里没有必要同时存在没有意义//void Print();//此版本不能传const修饰的参数void Print() const;//Date d1(d3)//Date(const Date d) {}//d1 d3//如果不想改变实参传引用时加const保护//void operator(const Date d) { //这种写法不支持连续赋值因为没有返回值// _year d._year;// _month d._month;// _day d._day;//}//Date operator(const Date d) { //如果是传值返回会调用拷贝构造。又因为出了作用域*this还在。所以可以改为传引用返回//Date operator(const Date d) {// if (this ! d) { //加这个条件是为了防止自己给自己赋值// _year d._year;// _month d._month;// _day d._day;// }// return *this;//}Date operator(const Date d);//总结只读函数可以加const内部不涉及修改成员bool operator(const Date x2) const;bool operator(const Date x2) const;bool operator(const Date x2) const;bool operator(const Date x2) const;bool operator(const Date x2) const;bool operator!(const Date x2) const;Date operator(int day);Date operator(int day) const;Date operator-(int day);Date operator-(int day) const;//前置和后置不仅是运算符重载还构成函数重载//d1 - d1.operator()Date operator();//d1 - d1.operator(0)//加一个int参数进行占位跟前置构成函数重载进行区分//本质后置调用编译器进行特殊处理Date operator(int);Date operator--();Date operator--(int);int operator-(const Date d) const;//日期相减//const int fun() {}//这里const的作用是不能修改返回值。//但是传值返回 返回的是返回值的一份拷贝临时对象本身具有常性所以这里的const没有意义//只有传引用返回的const才有意义//8.取地址及const取地址操作符重载//这两个默认成员函数一般不用重新定义 编译器默认会生成。//这两个运算符一般不需要重载使用编译器生成的默认取地址的重载即可//只有特殊情况才需要重载比如想让别人获取到指定的内容//自定义分为两个版本一个是非const版本一个是const版本//Date* operator() {// return this;//}//const Date* operator() const {// return this;//}//void operator(ostream out);//因为不符合使用习惯所有不定义为成员函数private:int _year;int _month;int _day; };//调整为全局函数 //void operator(ostream out, const Date d); ostream operator(ostream out, const Date d);//支持连续流插入 istream operator(istream in, Date d);//这里的Date d不能加const因为从控制台提取数据后有可能要改变该内容 Date.cpp #define _CRT_SECURE_NO_WARNINGS #include Date.h//Date::部分是一个作用域解析符它表示 Date 是一个类名Date 构造函数属于 Date 类。 int Date::GetMonthDay(int year, int month) {//monthArray这个数组会大量重复调用加static延长声明周期避免重复创建//加const防止该数组值被修改const static int monthArray[13] { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//要先判断是否为2月否则每次都会先判是否为断闰年没意义if ((month 2) ((year % 4 0 year % 100 ! 0) || (year % 400 0)))return 29;return monthArray[month];//这里返回的是数组值的临时拷贝对象本身具有常性 }Date::Date(int year, int month, int day) {_year year;_month month;_day day;//检查日期是否合法if (month 1 || month12 || day1 || dayGetMonthDay(year, month))cout 非法日期 endl; }void Date::Print() const { //这里的const本质是void Date::Print(const Date* this) 不能修改this指向的对象cout _year 年 _month 月 _day 日 endl; }Date Date::operator(const Date d) {if (this ! d) {_year d._year;_month d._month;_day d._day;}return *this; } //d1 d2 //bool Date::operator(Date* this, const Date x2) bool Date::operator(const Date x2) const {if (_year x2._year) {return true;}else if (_year x2._year _month x2._month) {return true;}else if (_year x2._year _month x2._month _day x2._day) {return true;}else {return false;} }bool Date::operator(const Date x2) const {return _year x2._year _month x2._month _day x2._day; }bool Date::operator(const Date x2) const {return *this x2 || *this x2; }bool Date::operator(const Date x2) const {return !(*this x2);//逻辑取反就是 }bool Date::operator(const Date x2) const {return !(*this x2);//逻辑取反就是 }bool Date::operator!(const Date x2) const {return !(*this x2);//逻辑取反就是 ! }加复用加等该版本更好。该版本未处理day为负数的情况 //Date Date::operator(int day) { //出了该函数对象还在。如果返回Date会生成一份拷贝拷贝又要调用拷贝构造。所以返回引用 // //月进位 // //先加天数并判断是否需要进位 // _day day;//_day是目前天数day是加上的天数 // //如果目前天数大于目前年份月份天数那么要将现在的天数减去该月天数并且月份进位 // //重复上述步骤直到目前天数符合该月份要求 // while (_day GetMonthDay(_year, _month)) { // _day - GetMonthDay(_year, _month); // _month; // //年进位 // if (_month 13) { // _year; // _month 1; // } // } // return *this;//返回调用该函数的Date类的对象(this是指针) //}//可处理day为负数的版本 Date Date::operator(int day) { //出了该函数对象还在。如果返回Date会生成一份拷贝拷贝又要调用if (day 0)return *this - (-day);//加负的天数等于减正的天数//月进位//先加天数并判断是否需要进位_day day;//_day是目前天数day是加上的天数//如果目前天数大于目前年份月份天数那么要将现在的天数减去该月天数并且月份进位//重复上述步骤直到目前天数符合该月份要求while (_day GetMonthDay(_year, _month)) {_day - GetMonthDay(_year, _month);_month;//年进位if (_month 13) {_year;_month 1;}}return *this;//返回调用该函数的Date类的对象(this是指针) }Date Date::operator(int day) const { //实现正确的日期天数Date tmp(*this);//因为不能改变自己即调用该函数的对象所以要调用拷贝构造函数拷贝一份tmp day;return tmp;//tmp出了该函数作用域就不在了所以是传值返回 }加等复用加 //Date Date::operator(int day) { //实现正确的日期天数 // //因为不能改变自己即调用该函数的对象所以要调用拷贝构造函数拷贝一份 // Date tmp(*this); // //Date tmp *this;//等价上方都是拷贝构造 // tmp._day day; // while (tmp._day GetMonthDay(tmp._year, tmp._month)) { // tmp._day - GetMonthDay(tmp._year, tmp._month); // tmp._month; // //年进位 // if (tmp._month 13) { // tmp._year; // tmp._month 1; // } // } // return tmp;//tmp出了该函数作用域就不在了所以是传值返回 //} // //Date Date::operator(int day) { // //Date ret *this day; // //*this ret; // // //赋值重载版本 // *this *this day;//只有赋值重载能够完成的场景 // return *this; //} //总结复用更好。 //不管是哪一种的重载都拷贝了两次第一次当前对象拷贝给tmp第二次传值返回又拷贝一次。 //但是 复用没有发生额外的拷贝而复用时调用了上面提到拷贝了两次。该版本未处理负的天数 //Date Date::operator-(int day) { // _day - day;//目前天数减 要减的天数 // while (_day 0) { //减完后目前天数0都不对需要继续处理。借位 // --_month;//目前天数0要向月份借 // if (_month 0) { //如果月份都为0就要像年借 // --_year; // _month 12; // } // //目前天数向月份和年份借一次后年份和月份也可能发生变化。 // //将变化后的年月的天数加到剩余的目前天数。如果天数合法循环结束如果天数依然为负那么继续借。 // _day GetMonthDay(_year, _month); // } // return *this; //}//可处理负的天数版本 Date Date::operator-(int day) {if (day 0)return *this (-day);//减负的天数等于加正的天数_day - day;//目前天数减 要减的天数while (_day 0) { //减完后目前天数0都不对需要继续处理。借位--_month;//目前天数0要向月份借if (_month 0) { //如果月份都为0就要像年借--_year;_month 12;}//目前天数向月份和年份借一次后年份和月份也可能发生变化。//将变化后的年月的天数加到剩余的目前天数。如果天数合法循环结束如果天数依然为负那么继续借。_day GetMonthDay(_year, _month);}return *this; }Date Date::operator-(int day) const { //日期减天数Date tmp(*this);tmp - day;return tmp; }//d1 Date Date::operator() {*this 1;return *this;//前置返回以后的值//this指向对象出了该函数还在所以传引用返回 }//d1 Date Date::operator(int) {Date tmp(*this);*this 1;return tmp;//后置返回前的值//tmp出了该函数不在所以传值返回 } //无论是前置还是后置自身都要只是返回的值不一样Date Date::operator--() {*this - 1;return *this; } Date Date::operator--(int) {Date tmp(*this);*this - 1;return tmp; }int Date::operator-(const Date d) const { //日期相减Date max *this;Date min d;int flag 1;//this是左d是右。左大右小减出来结果是正的if (*this d) {min *this;max d;flag -1;}int n 0;while (min ! max) { //这里也可以用min max不过!的实现比实现要简单min;//让日期小的那个一天一天加直到等于大的那个日期n;}return n * flag; }//void Date::operator(ostream out) { //该函数参数顺序(Date* this, ostream out) // out _year / _month / _day endl; //} cout d1; 该函数依然无法调用因为是双操作数左操作数必须是第一个参数即cout 但该函数第一个操作数是调用该函数的对象d1//void operator(ostream out, const Date d) { // out d._year / d._month / d._day endl; //}//支持连续流插入 ostream operator(ostream out, const Date d) {out d._year / d._month / d._day endl;return out; }istream operator(istream in, Date d) {in d._year d._month d._day;return in; } Test.cpp #define _CRT_SECURE_NO_WARNINGS #include Date.hvoid TestData1() {Date d1(2024, 8, 20);d1.Print();Date d2;d2.Print();Date d3(2023, 13, 25);d3.Print(); }void TestData2() {Date d1(2024, 8, 20);//拷贝构造一个已经存在对象去初始化另一个要创建对象Date d2(d1);d1.Print();d2.Print();Date d3(2024, 7, 8);//赋值两个已经存在对象进行拷贝d1 d3;//编译器转换为d1.operator(d3)d1.Print(); }void TestData3() {Date d1(2024, 8, 21);d1 180;d1.Print();Date d2(2024, 8, 21);Date ret d2 180;ret.Print();//Date d3(2024, 8, 21);拷贝构造//Date ret d3;//Date ret(d3 180);//Date ret d3 180;赋值//Date ret;//ret d3 180;}void TestData4() {Date d1(2024, 8, 21);d1 - 2000;d1.Print();Date d2(2024, 8, 26);d2 -200;d2.Print();Date d3(2024, 8, 26);d3 - -200;d3.Print(); }void TestData5() {Date d1(2023, 7, 27);Date ret1 d1;//Date ret1 d1.operator(0);//可以显示调用ret1.Print();d1.Print();Date ret2 d1;//Date ret2 d1.operator();ret2.Print();d1.Print(); }void TestData6() {Date d1(2024, 8, 26);Date d2(1991, 12, 5);cout (d1 - d2) endl; }void TestData7() {//void Print();const Date d1(2024, 8, 26);//d1.Print();//无法调用。//d1被const修饰d1地址类型就是const Date*不能修改指向的内容。//d1调用Print函数是把d1的地址传过去而Print函数的参数本质是Date* this所以这里权限被放大了。//将函数改为void Print() const; 这样不论对象是否被const修饰都可以调用该函数。//没有被const修饰的对象是一种权限的缩小 }void TestData8() {Date d1(2024, 9, 2);Date d2(2024, 9, 3);//不支持这种打印方式因为d1是自定义类型//cout d1;//cin d1;重载以后依然不能用cout d1这种方式调用因为参数顺序不匹配//d1 cout;//虽然可以但不符合使用习惯改为全局定义并添加友元声明//cout d1;//支持连续流插入版本cout d1 d2;cin d1;cout d1; }int main() {//TestData1();//TestData2();//TestData3();//TestData4();//TestData5();//TestData6();//TestData7();TestData8();return 0; } 第七章const成员 将const修饰的“成员函数”称之为const成员函数const修饰类成员函数实际修饰该成员函数隐含的this指针表明在该成员函数中不能对类的任何成员进行修改。 class Date { public:Date(int year, int month, int day) {_year year;_month month;_day day;}void Print() {cout _year 年 _month 月 _day 日 endl;}//可以被const对象调用的版本//void Print() const { //这里的const本质是void Date::Print(const Date* this) 不能修改this指向的对象// cout _year 年 _month 月 _day 日 endl;//} private:int _year; // 年int _month; // 月int _day; // 日 };int main() {const Date d1(2022, 1, 13);d1.Print();//d1被const修饰d1地址类型就是const Date*不能修改指向的内容。//d1调用Print函数是把d1的地址传过去而Print函数的参数本质是Date* this所以这里权限被放大了。//将函数改为void Print() const; 这样不论对象是否被const修饰都可以调用该函数。//没有被const修饰的对象是一种权限的缩小return 0; } const成员函数重载 以顺序表作为例子 #include iostream #include assert.h using namespace std;struct SeqList { public:void PushBack(int x) {//扩容_a[_size] x;}//版本1int operator[](size_t i) { //返回数组i下标处的值assert(i _size);return _a[i];//这里返回的是_a[i]的拷贝即临时对象具有常性。不能修改//_a 是类的私有成员外部无法直接访问。通过 operator[] 提供一个接口能在控制访问的同时保护数据的封装性。}//版本2//上方数组下标运算符重载是传值返回只能读不能写。//又因为数组开辟在堆上出了作用域还在可以传引用返回。//且这样做的好处可以修改即实现等操作int operator[](size_t i) {assert(i _size);return _a[i];}size_t size() { return _size; }//不能直接访问类的私有成员所以需要函数调用private:int* _a (int*)malloc(sizeof(int) * 10);size_t _size 0;size_t _capacity 0; };int main() {SeqList s;s.PushBack(1);s.PushBack(2);s.PushBack(3);s.PushBack(4);for (size_t i 0; i s.size(); i) cout s[i] endl;return 0; } 上方数组下标运算符重载函数在添加了打印数据函数后出现新的问题打印数组是不会修改数组的数据所以打印函数的参数一般都加const struct SeqList { public:void PushBack(int x) {//扩容_a[_size] x;}size_t size() { return _size; }//不能直接访问类的私有成员所以需要函数调用int operator[](size_t i) {assert(i _size);return _a[i];}private:int* _a (int*)malloc(sizeof(int) * 10);size_t _size 0;size_t _capacity 0; };//只是打印所以加const防止修改。且传值要拷贝开销太大所以传引用传参 void Print(const SeqList sl) {//这里的sl是const对象不能调用非const成员函数 size_t size(); 和下标重载运算符函数//所以要修改上方的size函数和运算符重载函数for (size_t i 0; i sl.size(); i)cout sl[i] ;cout endl; }int main() {SeqList s;s.PushBack(1);s.PushBack(2);s.PushBack(3);s.PushBack(4);for (size_t i 0; i s.size(); i)cout s[i] endl;Print(s);return 0; } 当打印函数的参数加上const后出现新问题打印函数的参数sl是const对象不能调用非const成员函数 size_t size(); 和下标重载运算符函数。所以size函数和运算符重载函数都要加const #include iostream #include assert.h using namespace std;struct SeqList { public:void PushBack(int x) {//扩容_a[_size] x;}//下方打印函数的参数是const对象且需要调用该函数所以该函数的参数也要加constsize_t size() const { return _size; }//该运算符重载函数和上方size函数同理也需要对参数加constint operator[](size_t i) const {assert(i _size);return _a[i];}private:int* _a (int*)malloc(sizeof(int) * 10);size_t _size 0;size_t _capacity 0; };//只是打印所以加const防止修改。且传值要拷贝开销太大所以传引用传参 void Print(const SeqList sl) {//这里的sl是const对象不能调用非const成员函数 size_t size(); 和下标重载运算符函数//所以要修改上方的size函数和运算符重载函数for (size_t i 0; i sl.size(); i)cout sl[i] ;cout endl; }int main() {SeqList s;s.PushBack(1);s.PushBack(2);s.PushBack(3);s.PushBack(4);for (size_t i 0; i s.size(); i)cout s[i] endl;Print(s);return 0; } 上方代码仍有bug未修复虽然打印函数里sl对象被const修饰但仍可以修改数组的数据 #include iostream #include assert.h using namespace std;struct SeqList { public:void PushBack(int x) {//扩容_a[_size] x;}size_t size() const { return _size; }int operator[](size_t i) const {assert(i _size);return _a[i];}private:int* _a (int*)malloc(sizeof(int) * 10);size_t _size 0;size_t _capacity 0; };//只是打印所以加const防止修改。且传值要拷贝开销太大所以传引用传参 void Print(const SeqList sl) {for (size_t i 0; i sl.size(); i) {sl[i];//虽然sl是const对象但这里合法且可以正常运行//解答const修饰sl指的是不能修改成员变量即_a_size_capacity_a是指针不能修改它的指向// 但sl[i]是数组里面的数据可以被修改cout sl[i] ;}cout endl; }int main() {SeqList s;s.PushBack(1);s.PushBack(2);s.PushBack(3);s.PushBack(4);for (size_t i 0; i s.size(); i)cout s[i] endl;Print(s);return 0; } 解决方法对[]函数 构造函数重载。这里的const对象调用有const修饰返回值的运算符重载函数这样可以避免对象被修改。也就是说即需要一个可读可写的下标重载运算符函数PushBack函数需要也需要一个只能读的下标重载运算符函数Print函数需要 #include iostream #include assert.h using namespace std;struct SeqList { public:void PushBack(int x) {//扩容_a[_size] x;}size_t size() const { return _size; }//int operator[](size_t i) const {// assert(i _size);// return _a[i];//}//如果只有上方的下标运算符重载函数则会出现之前提到在打印函数里可以完成s[i]的bug//但仅仅只是用const修饰返回值的下标运算符重载函数则无法实现s[i]//所以需要对[]运算符重载函数 再构造一个重载函数//需要修改返回值调用这个不想修改返回值调用下方int operator[](size_t i) {assert(i _size);return _a[i];}//基于下方Print函数的原因需要对返回值加const修饰防止修改const int operator[](size_t i) const {assert(i _size);return _a[i];}private:int* _a (int*)malloc(sizeof(int) * 10);size_t _size 0;size_t _capacity 0; };//只是打印所以加const防止修改。且传值要拷贝开销太大所以传引用传参 void Print(const SeqList sl) {for (size_t i 0; i sl.size(); i) {//sl[i];//报错不能给常量赋值cout sl[i] ;}cout endl; }int main() {SeqList s;s.PushBack(1);s.PushBack(2);s.PushBack(3);s.PushBack(4);for (size_t i 0; i s.size(); i)cout s[i] endl;Print(s);return 0; } 在 C 中成员函数的 const 修饰符用于控制对象的可变性和访问权限【非 const 对象】 可以调用 const 和非 const 成员函数。 const 成员函数承诺不修改对象的状态因此它们可以被非 const 对象调用也可以确保在调用时对象的状态不会被修改。【const 对象】 只能调用 const 成员函数。因为 const 对象不能修改其状态 所以只能调用那些不会修改对象内部数据的成员函数。非 const 成员函数可能会改变对象的状态这对于 const 对象是不允许的。 总结const 成员函数保证不会修改对象的状态适用于 const 对象非 const 成员函数可以修改对象状态适用于非 const 对象。 【const 成员函数】必须保证不修改对象的状态即不能修改成员变量。 因此const 成员函数中只能调用其他的 const 成员函数以确保不违反这一保证。 如果 const 成员函数尝试调用非 const 成员函数编译器会报错因为非 const 成员函数可能会修改对象的状态从而与 const 成员函数的承诺冲突。【非 const 成员函数】没有限制可能会修改对象的状态因此它可以调用 const 成员函数。 调用 const 成员函数不会引发矛盾因为 const 成员函数本身不会修改对象的状态不会对非 const 成员函数的修改行为产生影响。   第八章取地址及const取地址操作符重载 这两个默认成员函数一般不用重新定义 编译器默认会生成。 class Date { public://需要构成重载一个为非const对象准备另一个为const对象准备Date* operator() {return this;}const Date* operator()const {return this;} private:int _year; // 年int _month; // 月int _day; // 日 }; 这两个运算符一般不需要重载使用编译器生成的默认取地址的重载即可只有特殊情况才需要重载比如想让别人获取到指定的内容。 作业 1. 若要对data类中重载的加法运算符成员函数进行声明下列选项中正确的是 A.Data operator(Data); B.Data operator(Data); C.operator(Data,Data); D.Data(Data); 答案A A.正确 B.语法错误缺少运算符 C.成员函数参数过多 D.没有运算符重载关键字operator 2. 下列关于赋值运算符“”重载的叙述中正确的是( ) A.赋值运算符只能作为类的成员函数重载 B.默认的赋值运算符实现了“深层复制”功能 C.重载的赋值运算符函数有两个本类对象作为形参 D.如果己经定义了复制拷贝构造函数就不能重载赋值运算符 答案A A. 赋值运算符在类中不显式实现时编译器会生成一份默认的此时用户在类外再将赋值运算符重载为全局的就和编译器生成的默认赋值运算符冲突了故赋值运算符只能重载成成员函数 B.默认的赋值运算符是按成员成员属于浅赋值 C.参数只有一个另一个通过this指针传递 D.两个函数的调用场景不同相互没有影响 3. 哪个操作符不能被重载 ( )   A.* B.() C.. (点) D.[] E.- 答案C A.可以例如重载对象取值典型有以后学到的智能指针 B.可以例如以后学到的仿函数就是通过重载()实现的 C.不能不能被重载的运算符只有5个 点号. 三目运算?: 作用域访 问符:: 运算符sizeof 以及.* D.可以例如重载对象的指向典型有以后学到的智能指针 4. 在重载一个运算符为成员函数时其参数表中没有任何参数这说明该运算符是 A.无操作数的运算符 B.二元运算符 C.前缀一元运算符 D.后缀一元运算符 答案C A.重载为成员函数时其函数的参数个数与真实的函数参数个数会减少1个减少的则 通过this指针进行传递所以无参  则说明有一个参数故错误 B.无参成员函数相当于有一个参数的全局函数,不能是二元运算符 C.正确 D.区分前缀后缀时后缀运算需要加一个int参数 5. 已知表达式a中的是作为成员函数重载的运算符,则与a等效的运算符函数调用形式为( ) A.a.operator() B.a.operator(0) C.a.operator(int) D.operator(a,0) A.正确 B.operator()传递了整形参数故为后置,错误 C.调用函数传递类型导致语法错误 D.参数过多语法错误 6. 假设 AA 是一个类 AA* abc () const 是该类的一个成员函数的原型。若该函数返回 this 值当用 x.abc ()调用该成员函数后 x 的值是 A.可能被改变 B.已经被改变 C. 受到函数调用的影响 D.不变 A.此成员函数被定义为const常方法代表在函数内部不能修改任何当前对象的数据成员因此x不可能改变 B.错误不能被改变 C.x的值在函数内部不受任何影响 D.正确
http://www.hkea.cn/news/14472960/

相关文章:

  • msn网站制作2345网址导航下载桌面
  • 网站后台设计教程视频亚马逊平台的运营模式
  • 网站开发的形式有附近建网站公司
  • 网站链接优化用别人公司域名做网站
  • 北京做手机网站建设吉林移动网站
  • 个人网站做电影资源链接犯法吗山大优秀网站建设2018年度
  • 太原网站建设需求多嘛企业服务网站建设需要多少钱
  • 上海城乡建设网站证件查询国内永久免费saascrm
  • 深圳建立企业网站wordpress 计数
  • 网站推广烟台公司电话长春招聘网智联
  • 建自己的网站用多少钱网站原创文章规范
  • wordpress footer设置重庆seo博客
  • 万网怎么更改网站名字的微平台是什么意思
  • 网站建设上机考试题目网站seo优化外包
  • 黄陂建设网站路由器做网站教程
  • 一流的聊城网站建设中国网站备案
  • 做网站济南西开源自动化运维平台
  • 里水网站开发池州专业网站建设怎么样
  • 社区网站怎么建服装网站开发方案swot
  • 济南网站建设招聘东营网站建设铭盛信息
  • 上海知名装修公司排名榜河南seo
  • window服务器如何做网站访问免费个人博客网站模板下载
  • 沈阳 商城 网站 开发网站设计分析报告
  • 城乡住房规划建设局网站建立一个平台需要几部分
  • 威海网站建设吧建工教育网
  • 企业网站怎么扣费的WordPress分类目录 前100篇
  • 公路水运建设质量与安全监督系统网站开贴纸网站要怎么做
  • 手机兼职平台app排行榜前十名哈尔滨做网站seo的
  • 新纪实网站建设wordpress 产品页 如何关联
  • 潍坊高新区建设局网站成都app定制开发公司