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

桂林网站建设服务电话二手商品网站开发背景

桂林网站建设服务电话,二手商品网站开发背景,郑州承接各类网站建设,wordpress图片放大代码【本节目标】 1. string类的模拟实现 2.C基本类型互转string类型 3.编码表 #xff1a;值 --- 符号对应的表 4.扩展阅读 1. string类的模拟实现 1.1 经典的string类问题 上面已经对string类进行了简单的介绍#xff0c;大家只要能够正常使用即可。在面试中#xff0c;… 【本节目标】 1. string类的模拟实现 2.C基本类型互转string类型 3.编码表 值 --- 符号对应的表 4.扩展阅读 1. string类的模拟实现 1.1 经典的string类问题 上面已经对string类进行了简单的介绍大家只要能够正常使用即可。在面试中面试官总喜欢让学生自己 来模拟实现string类最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以 下string类的实现是否有问题为了防止和库里面的string类发生冲突我们在这里使用命名空间来限制我们写的string类。 构造函数 namespace yu {class string{public:string(const char* str):_str(str){}private:char* _str;size_t _size;size_t _capacity;}; } 上面的代码有什么问题吗 我们之前提到权限可以缩小可以平移但是就是不能放大那我们下面的写法还有错误吗 namespace yu {class string{public:string(const char* str):_str(str){}private:const char* _str;size_t _size;size_t _capacity;};void test(){string str(hello world);} } 这里也是不可以的因为常量字符串存在代码区只能可读不能写那我们上面就只能完成一个打印输出的工作不能完成扩容修改等其他增删改操作。所以我们可以开辟一个同样的空间 namespace yu {class string{public:string(const char* str)//strlen求取\0之前字符的个数:_str(new char[strlen(str)1]),_size(strlen(str))//capacity是存储有效字符的个数不包括\0,_capacity(strlen(str)){}private:char* _str;size_t _size;size_t _capacity;};void test(){string str(hello world);} } 但是上面strlen这个需要计算3次而且strlen的实践复杂度是O(N)所以我们写成下面的形式。 namespace yu {class string{public:string(const char* str):_size(strlen(str))//capacity是存储有效字符的个数不包括\0,_capacity(_size)//strlen求取\0之前字符的个数,_str(new char[_capacity 1]){}private:char* _str;size_t _size;size_t _capacity;};void test(){string str(hello world);} } 随后我们写一下c_str函数看看是否打印输出成功。 namespace yu {class string{public:string(const char* str):_size(strlen(str))//capacity是存储有效字符的个数不包括\0,_capacity(_size)//strlen求取\0之前字符的个数,_str(new char[_size 1]){strcpy(_str, str);//拷贝}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity;};void test(){string str(hello world);cout str.c_str() endl;} } int main() {yu::test();return 0; } 此时我们的程序发生了崩溃因为初始化的顺序和声明的顺序一致所以程序会先执行_str(new char[_capacity 1])但是此时_capacity还没有初始化此时编译器可能给了随机值或者0。 那么此时开的空间就只有1个字符的空间开空间小导致拷贝时程序报错。 那怎么解决呢我们可以初始化的顺序和声明的顺序一致。 但是这样的写法不好我们这里可以不使用初始化列表可以使用函数体内初始化。 namespace yu {class string{public:string(const char* str){_size strlen(str);_capacity _size;_str new char[_capacity 1];strcpy(_str, str);//拷贝}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity;};void test(){string str(hello world);cout str.c_str() endl;} } int main() {yu::test();return 0; } 我们再来来实现一下析构函数 ~string() {delete[] _str;_str nullptr;_capacity 0;_size 0; } string类里面还提供了无参的构造函数 namespace yu {class string{public:string():_str(nullptr),_size(0),_capacity(0){}string(const char* str){_size strlen(str);_capacity _size;_str new char[_capacity 1];strcpy(_str, str);//拷贝}~string(){delete[] _str;_str nullptr;_capacity 0;_size 0;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity;};void test(){string str;cout str.c_str() endl;} } int main() {yu::test();return 0; } 我们这里程序又崩溃了为什么cout在识别到char *类型的时候会认为当前输出的是字符串会进行解引用行为这里报错就是空指针解引用的原因。所以我们这里可以设置一个空间存储\0 string():_str(new char[1]),_size(0),_capacity(0){_str[0] \0;} 但是实践上我们一般写成全缺省构造函数不分别写有参和无参两种形式。 //string(const char* str nullptr)//error:strlen(nullptr)会报错 //string(const char* str \0)//error:char不能给char* string(const char* str )//常量字符串默认结尾是\0 {_size strlen(str);_capacity _size;_str new char[_capacity 1];strcpy(_str, str);//拷贝 } 再来实现一下size和[ ]操作符重载库中我们还实现了cosnt[ ]操作符重载形式这种形式函数内部未对对象*this作出改变所以可以加上const。 //不包括\0 size_t size() const {return _size; }//返回pos位置值的引用 // 1.减少拷贝 // 2.修改返回值 char operator[](size_t pos) {//这里可以因为\0处也有空间//hello world\0//\0位置处的下标就是_sizeassert(pos _size);return _str[pos]; } const char operator[](size_t pos) const {//这里可以因为\0处也有空间//hello world\0//\0位置处的下标就是_sizeassert(pos _size);return _str[pos]; } 函数内部未对对象*this作出改变所以可以加上const我们可以验证一下。 void test() {string str(hello world);for (size_t i 0; i str.size(); i){str[i];}cout str.c_str() endl; } 运行结果 除了上面的[ ]可以遍历和修改迭代器也可以修改我们来模拟实现一下。 typedef char* iterater; iterater begin() {//第一个字符位置是beginreturn _str; } iterater end() {//\0位置就是endreturn _str _size; } 我们来测试一下 void test() {string str(hello world);string::iterater it str.begin();while (it ! str.end()){cout *it;it;} } 运行结果 但是上面这种写法只适合底层空间连续后面遇到不连续的我们就要修改写法除了上面的打印工作我们还有范围for。 for (auto ch : str) {cout ch; } 其实范围for底层也是用的迭代器通过反汇编我们可以看到。 如果我们上面把begin变成Begin此时范围for就会报错因为范围for是傻瓜式的替换成迭代器只有我们自定义写的迭代器没有按照规则命名范围for就不能使用。 我们再来实现一下打印输出的工作 void print_str(const string s) {for (size_t i 0; i s.size(); i){s[i];}cout s.c_str() endl; } 但是我们发现我们的代码出现错误了为什么 因为我们上面的size和[ ]操作符重载传入的对象是非const类型的而我们的打印输出是const类型的这里会存在权限放大的方法所里这里会报错所以size和c_str函数内部未对对象*this作出改变所以可以加上const。而[ ]操作符重载可以使用cosnt版本的。  void print_str(const string s) {for (size_t i 0; i s.size(); i){//s[i];//此时是const也就不能修改cout s[i];}cout endl; } 运行结果 我们再将迭代器放入刚刚的输出打印函数我们发现也出现了同样的问题。 所以这里要使用const迭代器所以我们要实现一下。 typedef const char* const_iterater; const_iterater begin() const {return _str; } const_iterater end() const {return _str _size; } 因此我们的程序就可以正常输出但是此时指针指向的内容不可被改变。 我们再来实现一下string类的增删查改。字符串的增加操作必定都要开空间对于字符串追加的函数我们这里不能实现每次开2倍的空间操作如果要追加的字符串的长度过长开辟的空间必定不够因此这里我们先实现reserve函数解决空间开辟的问题。 void reserve(size_t n) {if (n _capacity){//扩容步骤/*1.开辟空间2.拷贝数据3.释放旧空间4.指向新空间*/char* tmp new char[n 1];//多开一个给\0的位置strcpy(tmp, _str);//会拷贝\0delete[] _str;_str tmp;_capacity n;}//不缩容return; } 现在预备条件已经写好了我就可以开始写轮子了。 void append(const char* str) {size_t len strlen(str);//这里都不包含\0因此可以不用处理//而且我们开空间都给\0开好了位置//空间永远都比capacity多一个if (_size len _capacity){reserve(_size len);}strcpy(_str _size, str);_size len;//这里插入的str字符串已经拷贝过来\0,就不需要单独处理了 } void push_back(char ch) {if (_size _capacity){//当上面构造函数没有传参时capacity值为0这里需要单独处理一下size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] ch;_size;_str[_size] \0;//处理\0 } //这里我们就可以复用上面的接口 string operator(const char* str) {append(str);return *this; } string operator(char ch) {push_back(ch);return *this; } 验证一下 void test() {string str(hello world);str !;str !!;print_str(str); } 运行结果 我们再来实现一下插入函数 void insert(size_t pos, char ch) {assert(pos _size);if (_size _capacity){//当上面构造函数没有传参时capacity值为0这里需要单独处理一下size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}size_t end _size;while (end pos){//依次向后挪动_str[end 1] _str[end];--end;}_str[pos] ch;_size;//挪动的时候挪动了\0//_str[_size] \0;这里不用处理 }void test() {string str(hello world);str !;str !!;print_str(str);cout endl;str.insert(5, *);str.insert(5, *);str.insert(5, *);print_str(str); } 运行结果 我们看一下我们的代码有没有什么问题我们试一下我们的头插str.insert(0, *); 头插的时候end减到为0下次减减的时候减到-1但是此时end为size_t会变成整型的最大值因此程序会一直运行进入死循环那我们将上面的end变量类型变为int呢我们来看一下下面的程序。 上面的结果为什么是yes当无符号整型和有符号整型比较时有符号整型会隐式提升为无符号整型-1此时就能转化为整型的最大值。所以将上面的end变量类型变为int也不行因为我们的pos也是无符号整型不过我们可以通过强制类型转换解决。 void insert(size_t pos, char ch) {assert(pos _size);if (_size _capacity){//当上面构造函数没有传参时capacity值为0这里需要单独处理一下size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}int end _size;while (end (int)pos){//依次向后挪动_str[end 1] _str[end];--end;}_str[pos] ch;_size;//挪动的时候挪动了\0//_str[_size] \0;这里不用处理 } 或者也可以这样写这样end变量的值只能减到0就不会出现上面的错误。 void insert(size_t pos, char ch) {assert(pos _size);if (_size _capacity){//当上面构造函数没有传参时capacity值为0这里需要单独处理一下size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}size_t end _size 1;while (end pos){//依次向后挪动_str[end] _str[end - 1];--end;}_str[pos] ch;_size;//挪动的时候挪动了\0//_str[_size] \0;这里不用处理 } 我们再来实现一下字符串的插入。 void insert(size_t pos, const char* str) {assert(pos _size);size_t len strlen(str);if (_size len _capacity){reserve(_size len);}int end _size;while (end (int)pos){_str[end len] _str[end];--end;}strncpy(_strpos,str,len); } 这里需要注意不能使用strcpy因为它会拷贝\0会覆盖后面的w字符。接下来我们再实现一下删除库里面接口为我们提供了缺省值npos它属于静态成员变量类里面定义类外初始化。 private:char* _str;size_t _size;size_t _capacity;//不用给缺省值static size_t npos;//不会初始化列表属于整个类属于所有对象 };size_t yu::string::npos -1; 但是我们发现库里面是这样写的。 我们发现当我们给双精度浮点型的时候程序报错了error C2864: yu::string::x: 带有类内初始化表达式的静态 数据成员 必须具有不可变的常量整型类型或必须被指定为“内联”。对于上面这种写法只能支持整型变量。上面的这种写法可以算是编译器的特殊处理此时的npos只读不能修改不能加加减减。 void erase(size_t pos, size_t len npos) {assert(pos _size);if (len npos || pos len _size){_str[pos] \0;_size pos;}else{strcpy(_str pos, _str pos len);_size - len;} } 测试一下 void test() {string str(hello world);print_str(str);cout endl;str.erase(5, 1);print_str(str);cout endl;str.erase(2);print_str(str); } 运行结果 我们看一下上面判断的条件第二个条件是否可以覆盖第一个条件。这里是不能的npos已经是最大值了如果再加上pos就会溢出程序就会有问题。我们再来写来写一下交换的成员函数。 void swap(string str) {//交换指向的内容即可std::swap(str._str, _str);std::swap(str._size, _size);std::swap(str._capacity, _capacity);//std::swap(str, *this);效率较低拷贝构造消耗大 } 我们来看一下库中这几个函数的区别如果没有第二个swap函数就只能调用第三个swap函数第三个函数首先要拷贝然后赋值这里都是深拷贝要开空间区拷贝代价大。                          我们再来实现一下find函数。 size_t find(char ch, size_t pos 0) {for (size_t i pos; i _size; i){if (_str[i] ch)return i;}return npos; } size_t find(const char* str, size_t pos 0) {//暴力匹配const char* ptr strstr(_str pos, str);if (ptr nullptr){return npos;}else{//指针相减是元素之间的个数return ptr - _str;} } 测试一下 void test1() {string str(hello world);cout str.find(h) endl;cout str.find(lo) endl; } 运行结果 我们再来实现一下substr函数。 string substr(size_t pos 0, size_t len npos) {assert(pos _size);size_t end pos len;if (len npos || pos len _size){end _size;}string str;str.reserve(end - pos);//小于后面字符的个数for (size_t i pos; i end; i){str _str[i];//会自动处理\0}return str; } 我么们来看一下我们的代码有没有什么问题 这里主要就是浅拷贝的问题临时对象和str对象指向了同一块空间。 说明上述String类没有显式定义其拷贝构造函数与赋值运算符重载此时编译器会合成默认的当用s1构 造s2时编译器会调用默认的拷贝构造。最终导致的问题是s1、s2共用同一块内存空间在释放时同一块 空间被释放多次而引起程序崩溃这种拷贝方式称为浅拷贝。 string(const string s)//拷贝构造 - 深拷贝 {_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity; } 通过深拷贝就可以解决问题指向同一块空间。 那赋值的深拷贝呢 string operator(const string s)//赋值 - 深拷贝 {if (this ! s){char* tmp new char[s._capacity 1];strcpy(tmp, s._str);delete[] _str;_str tmp;_size s._size;_capacity s._capacity;} } 此时也能解决问题。 我们上面都是通过C语言的形式中print_str或者str.c_str()输出我们的字符串的我们实现一下直接使用C流插入操作符来输出字符串。 //流插入和流提取重载在全局 ostream operator(ostream out, const string s) {for (auto ch :s){out ch;}return out; } 再来实现一下流提取。 // 这里会修改s所以不用带上const istream operator(iostream in, string s) {char ch;in ch;while (ch ! ch ! \n){s ch;in ch;} } 我们来测试一下 我们发现我们的程序无论是输入空格或者换行都不能结束。我们来测试一个数据当什么输入12345空格6安装我们想要的逻辑cin应该只会读取到”123345”。我们来一下我们的调式结果。 按照常理输入到5字符后应该输入 但是上面的调式结果却跳过了空格而是输出字符6ch没有拿到空格。 我们来看一下cin的特性当我们连续往两个字符中写入x 和ycin会将空格当为分隔符会忽略这个空格从而直接读取后续的字符也就是读取y\n也是如此我们上面的程序是将字符串分为一个个字符然后字符依次输入此时就是cin输入单个字符遇到空格或者换行就会被忽略掉所以上面的程序无论输入什么都不会被结束因为拿不到空格或者换行我们可以使用getchar来获取单个字符的输入但是我们今天学习的是C语言最好应用C函数。 我们这里使用我们的Cistream提供的get函数它通常不会跳过分隔符或空白字符而是将其留在输入流中因此我们就可以修改我们的流提取重载了。 // 这里会修改s所以不用带上const istream operator(iostream in, string s) {char ch in.get();//in ch;//拿不到空格或者换行while (ch ! ch ! \n){s ch;ch in.get();}return in; } 然后我们再来测试一下输入12345 678。 结果确实被显示出来了并且 678被输入到我们的缓冲区从而没有被显示在显示器上但是我们发现我们的流提取重载并没有清空之前存在的字符串因为我们实现的时候是使用了重载如果要输入的字符串之前没有清空那么后续输入的字符串就会在之前的字符串上追加。但是我们的库函数就是直接输入什么字符串就会显示什么字符串。 所以在输入字符串之前如果之前的字符串还有内容我们就要清空所以我们要实现一下我们的clear函数。 void clear() {//删除数据但是不会释放空间_size 0;_str[_size] \0; } 注意这里我们一定要将0位置处设置为\0否则就会出现错误。 然后再来改造一下我们的流提取重载。 // 这里会修改s所以不用带上const istream operator(iostream in, string s) {//清空历史数据s.clear();char ch in.get();//in ch;//拿不到空格或者换行while (ch ! ch ! \n){s ch;ch in.get();}return in; } 流提取重载这样就写好了但是还是有一个小问题如果我们要输入的字符非常长那我们就要经过多次开辟空间这样消耗很大那我们可以用reserve提前开辟空间吗不行因为我们不确定用户要输入的字符到底有多长同时我们也不能获取缓冲区输入字符的长度这里就有人设计出了一个字符数组来解决这个问题我们来看看是怎么设计的。 istream operator(iostream in, string s) {//清空历史数据s.clear();char buff[128];char ch in.get();int i 0;//in ch;//拿不到空格或者换行while (ch ! ch ! \n){buff[i] ch;//该数组出了作用域就被销毁if (i 127){//下标为127此时数组就有128个元素buff[i] \0;s ch;i 0;}ch in.get();}//i没有走到127的情况if (i 0){buff[i] \0;s buff;}return in; } 1.2.浅拷贝问题 浅拷贝也称位拷贝编译器只是将对象中的值拷贝过来。如果对象中管理资源最后就会导致多个对象共 享同一份资源当一个对象销毁时就会将该资源释放掉而此时另一些对象不知道该资源已经被释放以为 还有效所以当继续对资源进项操作时就会发生发生了访问违规。 就像一个家庭中有两个孩子但父母只买了一份玩具两个孩子愿意一块玩则万事大吉万一不想分享就 你争我夺玩具损坏。 可以采用深拷贝解决浅拷贝问题即每个对象都有一份独立的资源不要和其他对象共享。父母给每个孩 子都买一份玩具各自玩各自的就不会有问题了。 1.3 深拷贝问题 如果一个类中涉及到资源的管理其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情 况都是按照深拷贝方式提供。 1.4.传统版写法的String类的拷贝构造和赋值拷贝 string(const string s)//拷贝构造 - 深拷贝 {_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity; } string operator(const string s)//赋值 - 深拷贝 {if (this ! s){char* tmp new char[s._capacity 1];strcpy(tmp, s._str);delete[] _str;_str tmp;_size s._size;_capacity s._capacity;} } 1.5.现代版写法的String类的拷贝构造和赋值拷贝 string(const string s)//拷贝构造 - 深拷贝 {string tmp(s.c_str());swap(tmp); } 但是这里有些编译器可能会出现一个问题s2和tmp交换s2确实获得了tmp的数据但是tmp交换之后指向谁呢此时我们需要让他指向nullptr。 再来看一下赋值拷贝的现代写法。 // s1 s3 string operator(string s)//赋值 - 深拷贝 {swap(s);return *this; } 这里不能加引用因为加上引用s就是s3的别名此时就是交换是s1和s3。此时不带上引用s对象是通过s3对象拷贝构造的然后s再和s1交换s交换后指向s1当s出了作用域就会调用析构函数释放空间。 注意此时我们就不用考虑自己给自己赋值的问题因为此时我们传参的时候已经拷贝构造了。如果要考虑的话就要这样写 string operator(const string s)//赋值 - 深拷贝 {if (this ! s){string tmp(s);swap(tmp);} } 使用上面的赋值现代写法不能兼容我们的substr函数substr函数返回的是str的临时拷贝临时拷贝具有常属性而赋值现代写法参数是非cosnt这里会存在权限放大的原因所以会出现错误。 2.C基本类型转string函数 3.编码表 值 --- 符号对应的表 计算机中数据都是由二进制存储的那我们怎么通过这些01序列分辨出我们的数据是什么呢计算机中为我们提供了ASCII表。 比如今天我们要存储apple!实际上计算机存储的就是97 112 112 108 101 33 0这几个序列我们来验证一下。 此时的计算机只能存储显示英文那怎么显示其他国家的语言呢以我们国家为例。也要对应的值和对应的符号相对于起来但是中国文化上下五千年文明博大精深如果我们国家也用8个比特位一个字节表示一个符号也就是256中符号肯定不能存储文明国家所有的文字所有我们国家就用两个字节到四个字节表示一个文字一般常见的汉字可以考虑用两个字节对应一个汉字所以这样就有256*256个表示情况微软平台使用的都是GBK编码。 我们来看一下内存中是怎么样的通过两个字节去编码表找对应的汉字。 我们可以再来看一下编码表的顺序。 编码表不是乱编的是按照一定顺序编码的将同音字编码在一起。我们国家的编码表是兼容SASCII表的但是当同时出现中文和英文我们国家的编码表怎么识别呢它是当成两个字节去国家的编码去寻找呢还是当成一个字节去寻找呢在双字节编码表中英文字符会占用一个字节而中文字符会占用两个字节。在处理文本时系统可以通过检查字节的高位信息来确定是一个英文字符还是一个中文字符然后再在编码表中找到对应的字符。但是其他国家语言呢还需要兼容其他国家的编码表太繁琐了于是就衍生出来万国码。 万国码 广泛指的是 Unicode统一码而不是特指某一种具体的编码。Unicode 是一种用于文本字符的国际化标准目的是为了能够涵盖全球范围内的所有语言和符号。 编码方式: UTF-8 是一种可变长度编码方式使用1到4个字节来表示字符。对于ASCII字符使用一个字节表示对于其他字符使用更多的字节。 UTF-16 使用16位2字节来表示一个字符。基本多文本平面BMP上的字符使用16位表示。 UTF-32 是一种固定长度编码每个字符使用32位4字节表示不论字符在Unicode中的位置。 Linux下一般都使用UTF-8编码。 C标准库提供了多个字符串类型string、wstring、u16string和u32string以适应不同的字符编码需求。这些字符串类型是为了支持不同的字符集和编码方式 string std::string 是标准 C 中用于存储单字节字符的字符串类型。它使用了默认的字符集通常是 ASCII 或 UTF-8。 wstring std::wstring 是宽字符字符串类型在 Windows 平台上通常使用。它使用 wchar_t 类型存储字符这个类型在不同的编译器和平台上可能占据不同的字节大小例如Windows 上通常是 2 字节而在 Linux 上可能是 4 字节。 u16string std::u16string 是存储 UTF-16 编码的字符串类型每个字符通常占用 2 个字节。 u32string std::u32string 是存储 UTF-32 编码的字符串类型每个字符通常占用 4 个字节。 这些字符串类型的选择取决于需要处理的文本数据的特定要求。在多语言环境中特别是处理 Unicode 字符时选择适当的字符串类型非常重要。例如如果需要处理表情符号、不同语言的字符集或者需要支持各种语言的国际化应用程序那么使用宽字符或者 UTF-16/UTF-32 编码的字符串类型可能更为适合。 4.扩展阅读 面试中string的一种正确写法 STL中的string类怎么了
http://www.pierceye.com/news/324914/

相关文章:

  • 淘宝网站怎么做视频58做二手车网站应该怎么推广
  • 自动翻译网站软件cf刷枪网站怎么做的
  • 示范校建设验收网站网站对话窗口怎么做
  • phpcms 移动网站模板怎么做个人网页
  • 南宁手机建站公司新加坡网站后缀
  • 如何在建设部网站查询获奖情况如何申请电商网站
  • jsp网站访问万维网网站幻灯
  • 南通住房和城乡建设部网站首页安徽公司网站建设
  • 建筑论坛网站修改WordPress文章发布页面
  • 网站代备案系统seo优化服务是什么意思
  • 专门做选择题的网站一个网站seo做哪些工作
  • wordpress 多站点 拷贝中国建设银行春招网站
  • 门户营销型网站wordpress代码执行
  • 保山市建设厅网站做建筑机械网站那个网站好
  • 广告位网站建设国际人才网中山招聘网
  • 南昌市城市建设档案馆网站一个网站做无限关键词
  • wordpress特别卡 iis东莞推广优化公司
  • 做网站收入怎样开放平台登录
  • 外贸网站运营推广微信运营商
  • 国外做储物柜的网站做亚马逊网站一般发什么快递
  • 仿古建筑公司网站廊坊网站建设公司
  • 在线动画手机网站模板下载学软件开发需要什么基础
  • 北京的网站建设收费标准推广产品的方法和步骤
  • 北京市专业网站制作企业合肥做网络推广的公司
  • 网站建设php教程视频手机商城网站设计
  • 重庆网站建设公司哪个最好老家装设计网
  • 外贸网站建设产品crm公司
  • 网站做查赚钱网站建设捌金手指花总四
  • 有没有做链接的网站彩票型网站建设
  • 15年做哪个网站能致富网站界面设计的相关指南