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

长沙新能源建站补贴宁波南部商务区网站建设

长沙新能源建站补贴,宁波南部商务区网站建设,海底捞口碑营销案例,泰兴公司做网站文章目录 右值引用和移动语义的特性右值引用优化性能#xff0c;避免深拷贝移动(move )语义forward 完美转发emplace_back 减少内存拷贝和移动unordered container 无序容器map和unordered_map的差别内部实现机理不同优缺点以及适用处 小结优缺点以及适用处 小结 代… 文章目录 右值引用和移动语义的特性右值引用优化性能避免深拷贝移动(move )语义forward 完美转发emplace_back 减少内存拷贝和移动unordered container 无序容器map和unordered_map的差别内部实现机理不同优缺点以及适用处 小结优缺点以及适用处 小结 代码地址https://github.com/Phoenix8215/CplusplusMagicalCrafts 右值引用和移动语义 作用C11中引用了右值引用和移动语义可以避免无谓的复制提高了程序性能。 左值是表达式结束后仍然存在的持久对象右值是指表达式结束时就不存在的临时对象。 区分左值和右值的便捷方法是看能不能对表达式取地址如果能则为左值否则为右值 将亡值是C11新增的、与右值引用相关的表达式比如将要被移动的对象、T函数返回的 值、std::move返回值和转换成T的类型的转换函数返回值。 C11中的所有的值必将属于左值、将亡值、纯右值三者之一将亡值和纯右值都属于右值。 区分表达式的左右值属性如果可对表达式用符取址则为左值否则为右值。 左值 lvalue 是有标识符、可以取地址的表达式最常见的情况有 变量、函数或数据成员的名字返回左值引用的表达式如 x、x 1、cout ’ ’字符串字面量如 “hello world” 纯右值 prvalue 是没有标识符、不可以取地址的表达式一般也称之为“临时对象”。最常见的情况有 返回非引用类型的表达式如 x、x 1、make_shared(42)除字符串字面量之外的字面量如 42、true 的特性 右值引用就是对一个右值进行引用的类型。因为右值没有名字所以我们只能通过引用的方式找到它。 无论声明左值引用还是右值引用都必须立即进行初始化因为引用类型本身并不拥有所把绑定对象的内 存只是该对象的一个别名。 通过右值引用的声明该右值又“重获新生”其生命周期其生命周期与右值引用类型变量的生命周期一 样只要该变量还活着该右值临时量将会一直存活下去。 的总结如下 左值和右值是独立于它们的类型的右值引用类型可能是左值也可能是右值。auto 或函数参数类型自动推导的 T 是一个未定的引用类型被称为 universal references 它可能是左值引用也可能是右值引用类型取决于初始化的值类型。所有的右值引用叠加到右值引用上仍然是一个右值引用其他引用折叠都为左值引 用。当 T 为 模板参数时输入左值它会变成左值引用而输入右值时则变为具名的右 值引用。编译器会将已命名的右值引用视为左值而将未命名的右值引用视为右值。 右值引用优化性能避免深拷贝 对于含有堆内存的类我们需要提供深拷贝的拷贝构造函数如果使用默认构造函数会导致堆内存的 重复删除比如下面的代码 //2-1-memory #include iostream using namespace std; class A {public:A() :m_ptr(new int(0)) {cout constructor A endl;}~A(){cout destructor A, m_ptr: m_ptr endl;delete m_ptr;m_ptr nullptr;}private:int* m_ptr; }; // 为了避免返回值优化此函数故意这样写 A Get(bool flag) {A a;A b;cout ready return endl;if (flag)return a;elsereturn b; } int main() {{A a Get(false); // 运行报错}cout main finish endl;return 0; } /* constructor A constructor A ready return destructor A, m_ptr:0xf87af8 destructor A, m_ptr:0xf87ae8 destructor A, m_ptr:0xf87af8 main finish */在上面的代码中默认构造函数是浅拷贝main函数的 a 和Get函数的 b 会指向同一个指针 m_ptr在 析构的时候会导致重复删除该指针。正确的做法是提供深拷贝的拷贝构造函数比如下面的代码关闭 返回值优化的情况下 //2-1-memory2 #include iostream using namespace std; class A {public:A() :m_ptr(new int(0)) {cout constructor A endl;}A(const A a) :m_ptr(new int(*a.m_ptr)) {cout copy constructor A endl;}~A(){cout destructor A, m_ptr: m_ptr endl;delete m_ptr;m_ptr nullptr;}private:int* m_ptr; }; // 为了避免返回值优化此函数故意这样写 A Get(bool flag) {A a;A b;cout ready return endl;if (flag)return a;elsereturn b; } int main() {{A a Get(false); // 正确运行}cout main finish endl;return 0; } /* constructor A constructor A ready return copy constructor A destructor A, m_ptr:0xea7af8 destructor A, m_ptr:0xea7ae8 destructor A, m_ptr:0xea7b08 main finish */这样就可以保证拷贝构造时的安全性但有时这种拷贝构造却是不必要的比如上面代码中的拷贝构造 就是不必要的。上面代码中的 Get 函数会返回临时变量然后通过这个临时变量拷贝构造了一个新的对 象 b临时变量在拷贝构造完成之后就销毁了如果堆内存很大那么这个拷贝构造的代价会很大 带来了额外的性能损耗。有没有办法避免临时对象的拷贝构造呢答案是肯定的。看下面的代码 //2-1-memory3 #include iostream using namespace std; class A {public:A() :m_ptr(new int(0)) {cout constructor A endl;}A(const A a) :m_ptr(new int(*a.m_ptr)) {cout copy constructor A endl;}A(A a) :m_ptr(a.m_ptr) {a.m_ptr nullptr;cout move constructor A endl;}~A(){cout destructor A, m_ptr: m_ptr endl;if(m_ptr)delete m_ptr;}private:int* m_ptr; }; // 为了避免返回值优化此函数故意这样写 A Get(bool flag) {A a;A b;cout ready return endl;if (flag)return a;elsereturn b; } int main() {{A a Get(false); // 正确运行}cout main finish endl;return 0; }/* constructor A constructor A ready return move constructor A destructor A, m_ptr:0 destructor A, m_ptr:0xfa7ae8 destructor A, m_ptr:0xfa7af8 main finish */上面的代码中没有了拷贝构造取而代之的是移动构造 Move Construct。从移动构造函数的实现 中可以看到它的参数是一个右值引用类型的参数 A这里没有深拷贝只有浅拷贝这样就避免了 对临时对象的深拷贝提高了性能。这里的 A 用来根据参数是左值还是右值来建立分支如果是临时 值则会选择移动构造函数。移动构造函数只是将临时对象的资源做了浅拷贝不需要对其进行深拷 贝从而避免了额外的拷贝提高性能。这也就是所谓的移动语义 move 语义右值引用的一个重 要目的是用来支持移动语义的。 移动语义可以将资源堆、系统对象等通过浅拷贝方式从一个对象转移到另一个对象这样能够减少 不必要的临时对象的创建、拷贝以及销毁可以大幅度提高 C 应用程序的性能消除临时对象的维护 创建和销毁对性能的影响。 以一个简单的 string 类为示例实现拷贝构造函数和拷贝赋值操作符。 //2-1-mystring #include iostream #include vector #include cstdio #include cstdlib #include string.h using namespace std; class MyString {private:char* m_data;size_t m_len;void copy_data(const char *s) {m_data new char[m_len1];memcpy(m_data, s, m_len);m_data[m_len] \0;}public:MyString() {m_data NULL;m_len 0;}MyString(const char* p) {m_len strlen (p);copy_data(p);}MyString(const MyString str) {m_len str.m_len;copy_data(str.m_data);std::cout Copy Constructor is called! source: str.m_data std::endl;}MyString operator(const MyString str) {if (this ! str) {m_len str.m_len;copy_data(str.m_data);}std::cout Copy Assignment is called! source: str.m_data std::endl;return *this;}virtual ~MyString() {if (m_data) free(m_data);} }; void test() {MyString a;a MyString(Hello);std::vectorMyString vec;vec.push_back(MyString(World)); } int main() {test();return 0; }实现了调用拷贝构造函数的操作和拷贝赋值操作符的操作。MyString(“Hello”) 和 MyString(“World”) 都 是临时对象也就是右值。虽然它们是临时的但程序仍然调用了拷贝构造和拷贝赋值造成了没有意 义的资源申请和释放的操作。如果能够直接使用临时对象已经申请的资源既能节省资源有能节省资 源申请和释放的时间。这正是定义转移语义的目的。 用c11的右值引用来定义这两个函数 // 用c11的右值引用来定义这两个函数 MyString(MyString str) {std::cout Move Constructor is called! source: str.m_data std::endl;m_len str.m_len;m_data str.m_data; //避免了不必要的拷贝str.m_len 0;str.m_data NULL; } MyString operator(MyString str) {std::cout Move Assignment is called! source: str.m_data std::endl;if (this ! str) {m_len str.m_len;m_data str.m_data; //避免了不必要的拷贝str.m_len 0;str.m_data NULL;}return *this; }有了右值引用和转移语义我们在设计和实现类时对于需要动态申请大量资源的类应该设计右值引 用的拷贝构造函数和赋值函数以提高应用程序的效率。 移动(move )语义 我们知道移动语义是通过右值引用来匹配临时值的那么普通的左值是否也能借组移动语义来优化性 能呢C11为了解决这个问题提供了std::move()方法来将左值转换为右值从而方便应用移动语 义。move是将对象的状态或者所有权从一个对象转移到另一个对象只是转义没有内存拷贝。 //2-2-move1 #include iostream #include vector #include cstdio #include cstdlib #include string.h using namespace std; class MyString {private:char* m_data;size_t m_len;void copy_data(const char *s) {m_data new char[m_len1];memcpy(m_data, s, m_len);m_data[m_len] \0;}public:MyString() {m_data NULL;m_len 0;}MyString(const char* p) {m_len strlen (p);copy_data(p);}MyString(const MyString str) {m_len str.m_len;copy_data(str.m_data);std::cout Copy Constructor is called! source: str.m_data std::endl;}MyString operator(const MyString str) {if (this ! str) {m_len str.m_len;copy_data(str.m_data);}std::cout Copy Assignment is called! source: str.m_data std::endl;return *this;}// 用c11的右值引用来定义这两个函数MyString(MyString str) {std::cout Move Constructor is called! source: str.m_data std::endl;m_len str.m_len;m_data str.m_data; //避免了不必要的拷贝str.m_len 0;str.m_data NULL;}MyString operator(MyString str) {std::cout Move Assignment is called! source: str.m_data std::endl;if (this ! str) {m_len str.m_len;m_data str.m_data; //避免了不必要的拷贝str.m_len 0;str.m_data NULL;}return *this;}virtual ~MyString() {if (m_data) free(m_data);} }; int main() {MyString a;a MyString(Hello); // moveMyString b a; // copyMyString c std::move(a); // move 将左值转为右值return 0; } forward 完美转发 forward 完美转发实现了参数在传递过程中保持其值属性的功能即若是左值则传递之后仍然是左 值若是右值则传递之后仍然是右值。 现存在一个函数 Templateclass T void func(T val);根据前面所描述的这种引用类型既可以对左值引用亦可以对右值引用。 但要注意引用以后这个val值它本质上是一个左值 看下面例子 int a 10; int b a; //错误注意这里a是一个右值引用但其本身a也有内存名字所以a本身是一个左值再用右值引用引用a这 是不对的。 因此我们有了std::forward()完美转发这种T val中的val是左值但如果我们用std::forward (val) 就会按照参数原来的类型转发 int a 10; int b std::forwardint(a);这样是正确的 通过范例巩固下知识 //2-4-forward #include iostream using namespace std; template class Tvoid Print(T t) {cout L t endl; } template class Tvoid Print(T t) {cout R t endl; } template class Tvoid func(T t) {Print(t);Print(std::move(t));Print(std::forwardT(t)); } int main() {cout -- func(1) endl;func(1);int x 10;int y 20;cout -- func(x) endl;func(x); // x本身是左值cout -- func(std::forwardint(y)) endl;func(std::forwardint(y)); //return 0; } /* -- func(1) L1 R1 R1 -- func(x) L10 R10 L10 按照原来的属性转发 -- func(std::forward(y)) L20 R20 R20 */解释 func(1) 由于1是右值所以未定的引用类型Tv被一个右值初始化后变成了一个右值引用但是在 func()函数体内部调用PrintT(v) 时v又变成了一个左值因为在std::forward里它已经变成了一个具 名的变量所以它是一个左值因此示例测试结果第一个PrintT被调用打印出“L1 调用PrintTstd::forward(v)时由于std::forward会按参数原来的类型转发因此它还是一个右值 这里已经发生了类型推导所以这里的T不是一个未定的引用类型会调用void PrintTTt函 数打印 “R1”.调用PrintT(std::move(v))是将v变成一个右值v本身也是右值因此它将输出”R1 func(x)未定的引用类型Tv被一个左值初始化后变成了一个左值引用因此在调用 PrintT(std::forward(v))时它会被转发到void PrintTTt. forward将左值转换为右值 MyString str1 hello; MyString str2(str1); MyString str3 Fun(); MyString str4 move(str2); MyString str5(forwardMyString(str3));综合示例 #include stdio.h #include iostream #includevector using namespace std; class A {public:A() :m_ptr(NULL), m_nSize(0){}A(int *ptr, int nSize){m_nSize nSize;m_ptr new int[nSize];if (m_ptr){memcpy(m_ptr, ptr, sizeof(sizeof(int) * nSize));}}A(const A other) // 拷贝构造函数实现深拷贝{m_nSize other.m_nSize;if (other.m_ptr){delete[] m_ptr;m_ptr new int[m_nSize];memcpy(m_ptr, other.m_ptr, sizeof(sizeof(int)* m_nSize));}else{m_ptr NULL;}cout A(const int i) endl;}// 右值应用构造函数A(A other){m_ptr NULL;m_nSize other.m_nSize;if (other.m_ptr){m_ptr move(other.m_ptr); // 移动语义other.m_ptr NULL;}}~A(){if (m_ptr){delete[] m_ptr;m_ptr NULL;}}void deleteptr(){if (m_ptr){delete[] m_ptr;m_ptr NULL;}}int *m_ptr;int m_nSize; }; void main() {int arr[] { 1, 2, 3 };A a(arr, sizeof(arr)/sizeof(arr[0]));cout m_ptr in a Addr: 0x a.m_ptr endl;A b(a);cout m_ptr in b Addr: 0x b.m_ptr endl;b.deleteptr();A c(std::forwardA(a)); // 完美转换cout m_ptr in c Addr: 0x c.m_ptr endl;c.deleteptr();vectorint vect{ 1, 2, 3, 4, 5 };cout before move vect size: vect.size() endl;vectorint vect1 move(vect);cout after move vect size: vect.size() endl;cout new vect1 size: vect1.size() endl; } emplace_back 减少内存拷贝和移动 对于STL容器C11后引入了emplace_back接口。 emplace_back是就地构造不用构造后再次复制到容器中。因此效率更高。 考虑这样的语句 vectorstring testVec; testVec.push_back(string(16, a));上述语句足够简单易懂将一个string对象添加到testVec中。底层实现 首先string(16, ‘a’)会创建一个string类型的临时对象这涉及到一次string构造过程。其次vector内会创建一个新的string对象这是第二次构造。最后在push_back结束时最开始的临时对象会被析构。加在一起这两行代码会涉及到两次 string构造和一次析构。 c11可以用emplace_back代替push_backemplace_back可以直接在vector中构建一个对象而非 创建一个临时对象再放进vector再销毁。emplace_back可以省略一次构建和一次析构从而达到优 化的目的. //time_interval.h #ifndef TIME_INTERVAL_H #define TIME_INTERVAL_H #include iostream #include memory #include string #ifdef GCC #include sys/time.h #else #include ctime #endif // GCC class TimeInterval {public:TimeInterval(const std::string d) : detail(d){init();}TimeInterval(){init();}~TimeInterval(){#ifdef GCCgettimeofday(end, NULL);std::cout detail 1000 * (end.tv_sec - start.tv_sec) (end.tv_usec -start.tv_usec) / 1000 ms endl;#elseend clock();std::cout detail (double)(end - start) ms std::endl;#endif // GCC}protected:void init() {#ifdef GCCgettimeofday(start, NULL);#elsestart clock();#endif // GCC}private:std::string detail;#ifdef GCCtimeval start, end;#elseclock_t start, end;#endif // GCC }; #define TIME_INTERVAL_SCOPE(d) std::shared_ptrTimeInterval time_interval_scope_begin std::make_sharedTimeInterval(d)#endif // TIME_INTERVAL_H//2-5-emplace_back #include vector #include string #include time_interval.h int main() {std::vectorstd::string v;int count 10000000;v.reserve(count); //预分配十万大小排除掉分配内存的时间{TIME_INTERVAL_SCOPE(push_back string:);for (int i 0; i count; i){std::string temp(ceshi);v.push_back(temp);// push_back(const string)参数是左值引用}}v.clear();{TIME_INTERVAL_SCOPE(push_back move(string):);for (int i 0; i count; i){std::string temp(ceshi);v.push_back(std::move(temp));// push_back(string ), 参数是右值引用}}v.clear();{TIME_INTERVAL_SCOPE(push_back(string):);for (int i 0; i count; i){v.push_back(std::string(ceshi));// push_back(string ), 参数是右值引用}}v.clear();{TIME_INTERVAL_SCOPE(push_back(c string):);for (int i 0; i count; i){v.push_back(ceshi);// push_back(string ), 参数是右值引用}}v.clear();{TIME_INTERVAL_SCOPE(emplace_back(c string):);for (int i 0; i count; i){v.emplace_back(ceshi);// 只有一次构造函数不调用拷贝构造函数速度最快}} } /* push_back string:335 ms push_back move(string):307 ms push_back(string):285 ms push_back(c string):295 ms emplace_back(c string):234 ms */第1中方法耗时最长原因显而易见将调用左值引用的push_back且将会调用一次string的拷贝构造 函数比较耗时这里的string还算很短的如果很长的话差异会更大 第2、3、4中方法耗时基本一样参数为右值将调用右值引用的push_back故调用string的移动构造 函数移动构造函数耗时比拷贝构造函数少因为不需要重新分配内存空间。 第5中方法耗时最少因为emplace_back只调用构造函数没有移动构造函数也没有拷贝构造函数。 为了证实上述论断我们自定义一个类并在普通构造函数、拷贝构造函数、移动构造函数中打印相应 描述 #include vector #include string #include time_interval.h using namespace std; class Foo {public:Foo(std::string str) : name(str) {std::cout constructor std::endl;}Foo(const Foo f) : name(f.name) {std::cout copy constructor std::endl;}Foo(Foo f) : name(std::move(f.name)){std::cout move constructor std::endl;}private:std::string name; }; int main() {std::vectorFoo v;int count 10000000;v.reserve(count); //预分配十万大小排除掉分配内存的时间{TIME_INTERVAL_SCOPE(push_back T:);Foo temp(test);v.push_back(temp);// push_back(const T)参数是左值引用//打印结果//constructor//copy constructor}cout ---------------------\n endl;v.clear();{TIME_INTERVAL_SCOPE(push_back move(T):);Foo temp(test);v.push_back(std::move(temp));// push_back(T ), 参数是右值引用//打印结果//constructor//move constructor}cout ---------------------\n endl;v.clear();{TIME_INTERVAL_SCOPE(push_back(T):);v.push_back(Foo(test));// push_back(T ), 参数是右值引用//打印结果//constructor//move constructor}cout ---------------------\n endl;v.clear();{std::string temp test;TIME_INTERVAL_SCOPE(push_back(string):);v.push_back(temp);// push_back(T ), 参数是右值引用//打印结果//constructor//move constructor}cout ---------------------\n endl;v.clear();{std::string temp test;TIME_INTERVAL_SCOPE(emplace_back(string):);v.emplace_back(temp);// 只有一次构造函数不调用拷贝构造函数速度最快//打印结果//constructor} } /* constructor copy constructor push_back T:2 ms constructor move constructor push_back move(T):0 ms constructor move constructor push_back(T):0 ms constructor move constructor push_back(string):0 ms constructor emplace_back(string):0 ms */unordered container 无序容器 C11 增加了无序容器 unordered_map/unordered_multimap 和 unordered_set/unordered_multiset由于这些容器中的元素是不排序的因此比有序容器 map/multimap 和 set/multiset 效率更高。 map 和 set 内部是红黑树在插入元素时会自动排序而 无序容器内部是散列表 Hash Table通过哈希 Hash而不是排序来快速操作元素使得效率 更高。由于无序容器内部是散列表因此无序容器的 key 需要提供 hash_value 函数其他用法和 map/set 的用法是一样的。不过对于自定义的 key需要提供 Hash 函数和比较函数。 map和unordered_map的差别 需要引入的头文件不同 map: #include map unordered_map: #include unordered_map 内部实现机理不同 map map内部实现了一个红黑树红黑树是非严格平衡二叉搜索树而AVL是严格平衡二叉搜 索树红黑树具有自动排序的功能因此map内部的所有元素都是有序的红黑树的每一个节点 都代表着map的一个元素。因此对于map进行的查找删除添加等一系列的操作都相当于是对 红黑树进行的操作。map中的元素是按照二叉搜索树又名二叉查找树、二叉排序树特点就是左 子树上所有节点的键值都小于根节点的键值右子树所有节点的键值都大于根节点的键值存储 的使用中序遍历可将键值按照从小到大遍历出来。unordered_map: unordered_map内部实现了一个哈希表也叫散列表通过把关键码值映射到 Hash表中一个位置来访问记录查找的时间复杂度可达到O(1)其在海量数据处理中有着广泛应 用。因此其元素的排列顺序是无序的。 优缺点以及适用处 map 优点 有序性这是map结构最大的优点其元素的有序性在很多应用中都会简化很多的操作 红黑树内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现因此效率非 常的高缺点: 空间占用率高因为map内部实现了红黑树虽然提高了运行效率但是因为每一个节点都需要额 外保存父节点、孩子节点和红/黑性质使得每一个节点都占用大量的空间 适用处 对于那些有顺序要求的问题用map会更高效一些 unordered_map 优点 因为内部实现了哈希表因此其查找速度非常的快缺点 哈希表的建立比较耗费时间适用处对于查找问题unordered_map会更加高效一些因此遇到查找问题常会考虑一下用 unordered_map 总结 内存占有率的问题就转化成红黑树 VS hash表 , 还是unorder_map占用的内存要高。但是unordered_map执行效率要比map高很多对于unordered_map或unordered_set容器其遍历顺序与创建该容器时输入的顺序不一定相 同因为遍历是按照哈希表从前往后依次遍历的 小结 C11 在性能上做了很大的改进最大程度减少了内存移动和复制通过右值引用、 forward、 emplace 和一些无序容器我们可以大幅度改进程序性能。 右值引用仅仅是通过改变资源的所有者来避免内存的拷贝能大幅度提高性能。forward 能根据参数的实际类型转发给正确的函数。emplace 系列函数通过直接构造对象的方式避免了内存的拷贝和移动。 )其在海量数据处理中有着广泛应 用。因此其元素的排列顺序是无序的。 优缺点以及适用处 map 优点 有序性这是map结构最大的优点其元素的有序性在很多应用中都会简化很多的操作 红黑树内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现因此效率非 常的高缺点: 空间占用率高因为map内部实现了红黑树虽然提高了运行效率但是因为每一个节点都需要额 外保存父节点、孩子节点和红/黑性质使得每一个节点都占用大量的空间 适用处 对于那些有顺序要求的问题用map会更高效一些 unordered_map 优点 因为内部实现了哈希表因此其查找速度非常的快缺点 哈希表的建立比较耗费时间适用处对于查找问题unordered_map会更加高效一些因此遇到查找问题常会考虑一下用 unordered_map 总结 内存占有率的问题就转化成红黑树 VS hash表 , 还是unorder_map占用的内存要高。但是unordered_map执行效率要比map高很多对于unordered_map或unordered_set容器其遍历顺序与创建该容器时输入的顺序不一定相 同因为遍历是按照哈希表从前往后依次遍历的 小结 C11 在性能上做了很大的改进最大程度减少了内存移动和复制通过右值引用、 forward、 emplace 和一些无序容器我们可以大幅度改进程序性能。 右值引用仅仅是通过改变资源的所有者来避免内存的拷贝能大幅度提高性能。forward 能根据参数的实际类型转发给正确的函数。emplace 系列函数通过直接构造对象的方式避免了内存的拷贝和移动。无序容器在插入元素时不排序提高了插入效率不过对于自定义 key 时需要提供 hash 函数和比 较函数
http://www.dnsts.com.cn/news/82420.html

相关文章:

  • 做网站需要考虑哪些问题app排版网站
  • 建设个读书网站大约需要投入多少钱云网站注册
  • 大作设计网站官网登录入口wordpress微信登录申请
  • 常用网站开发语言搜索引擎优化排名技巧
  • 中国广东手机网站建设上海公司排名大全
  • 网站下载的app删除了怎么找到杭州响应式网站案例
  • 娄底企业网站建设制作连锁加盟网络营销公司
  • 重庆整合网络营销之整站优化wordpress小蜜蜂
  • 国际金融资讯做的好的网站手机评测哪个网站做的好点
  • 山东恒昆建设工程有限公司网站北京网站建设价格行情
  • 开发网站要注意什么哪些网站可以进行域名注册
  • 网站建设套模板视频海外电商平台
  • 免费网站是网站设网站设计
  • 专业类网站品牌宝网站认证
  • 网站服务器怎么建设网络营销毕业后做什么工作
  • 怎样弄免费网站高校网站建设需求分析报告
  • 做网站学习什么设计师自己做网站
  • 网站的流量是怎么回事wordpress点击显示微信二维码
  • 美丽寮步网站建设高性能wordpress后台框架
  • 淮南家居网站建设怎么样品牌网站制作网站公司
  • 海口建网站公司仿网站出售
  • 淘宝联盟怎么做网站推广ps网页界面设计
  • wordpress二維碼php网站好做seo
  • 做违法网站的后果太原seo关键词排名优化
  • 网站做短信验证需要多少钱网站流量统计主要指标包括
  • 网站开发员工资广州免费建站推荐
  • 有模板怎么做网站合肥网站建设黄页
  • 做公司集团网站网站建设合同用贴印花税吗
  • 开网站做商城怎么样vs2015做网站如何添加控件
  • 网站logo图怎么做wordpress引用文章