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

国内网站备案深圳哪家网站建设好seo1888

国内网站备案,深圳哪家网站建设好seo1888,太原手机网站开发,推广图片模板前言#xff1a; 在之前的学习过程中#xff0c;我们已经对继承进行了详细的学习和了解。今天#xff0c;我将带领大家学习的是关于 多态 的基本知识。 目录 #xff08;一#xff09;多态的概念 1、概念 #xff08;二#xff09;多态的定义及实现 1、多态的构成条…前言 在之前的学习过程中我们已经对继承进行了详细的学习和了解。今天我将带领大家学习的是关于 多态 的基本知识。 目录 一多态的概念 1、概念 二多态的定义及实现 1、多态的构成条件 2、虚函数 1️⃣纯虚函数 2️⃣ 面试题虚函数与纯虚函数的区别 3、虚函数的重写 1️⃣虚函数重写的两个例外 2️⃣析构函数的重写(基类与派生类析构函数的名字不同) 4、C11 override 和 final 5、重载、覆盖(重写)、隐藏(重定义)的对比面试题 三抽象类 1、概念 2、接口继承和实现继承 四多态的原理 1、虚函数表 2、多态的原理 3、动态绑定与静态绑定 总结 一多态的概念 1、概念 多态的概念通俗来说就是多种形态具体点就是去完成某个行为当不同的对象去完成时会 产生出不同的状态。 举个栗子比如买票这个行为 当 普通人 买票时是全价买票 学生 买票时是半价买票 军人 买票时是优先买票。 再举个栗子 最近为了 争夺在线支付市场 支付宝年底经常会做诱人的 扫红包 - 支付 - 给奖励金 的 活动。那么大家想想为什么有人扫的红包又大又新鲜8 块、 10 块 ... 而有人扫的红包都是 1 毛 5 毛 .... 。其实这背后也是一个多态行为。支付宝首先会分析你的账户数据比如你是新用户、比如 你没有经常支付宝支付等等那么你需要被鼓励使用支付宝那么就你扫码金额 random()%99比如你经常使用支付宝支付或者支付宝账户中常年没钱那么就不需要太鼓励你 去使用支付宝那么就你扫码金额 random()%1 总结一下 同样是扫码动作不同的用户扫 得到的不一样的红包这也是一种多态行为。 ps 支付宝红包问题纯属瞎编大家仅供娱乐。 【小结】 1、这些例子中不同的对象根据自身的特性和行为对相同的消息做出了不同的响应展现了多态的概念 2、通过多态性我们可以灵活地处理不同的对象并针对每个对象的特点执行适当的操作提高代码的可扩展性和复用性。 二多态的定义及实现 1、多态的构成条件 多态是在不同继承关系的类对象去调用同一函数产生了不同的行为。比如 Student 继承了 Person 。 Person 对象买票全价 Student 对象买票半价。 那么在继承中要 构成多态还有两个条件 1. 必须通过基类的指针或者引用调用虚函数 2. 被调用的函数必须是虚函数且派生类必须对基类的虚函数进行重写 【小结】 要实现多态性需要满足以下条件 继承关系存在一个继承关系的类层次结构包括基类和派生类。派生类继承了基类的属性和方法。 方法重写在派生类中重新定义重写与基类中相同名称的方法。子类通过重写基类方法来赋予自己独特的行为。 向上转型将派生类的对象赋值给基类的引用变量。这样可以使得基类引用指向派生类的对象从而可以调用派生类中重写的方法。 运行时绑定在运行时确定调用哪个方法实现动态绑定。由于基类引用指向的是派生类对象因此根据实际的对象类型来决定调用哪个子类的方法。 当满足以上条件时就实现了多态性。通过使用多态可以提高代码的灵活性、可扩展性和可维护性同时减少了代码的重复编写。 2、虚函数 虚函数  即被virtual修饰的类成员函数称为虚函数  【注意】 这里有一个大家容易混淆的点那就是虚函数与之前学到的虚继承之间有什么联系吗强调二者之间无联系只是公用一个关键字而已 C中的虚函数和虚继承是两个不同的概念它们在面向对象编程中发挥不同的作用。 下面是对这两个概念的解释以及它们之间的关系 虚函数Virtual Functions 虚函数是用于实现动态多态性的机制。通过将基类中的函数声明为虚函数可以在派生类中重写override该函数来实现不同的行为。当通过基类指针或引用调用虚函数时实际执行的函数取决于指针或引用指向的对象的类型而不是指针或引用本身的类型。通过使用虚函数可以在运行时决定调用哪个函数实现对象的多态行为。 虚继承Virtual Inheritance 虚继承是解决多继承带来的问题的一种机制。当一个类需要从多个基类继承时如果其中的一些基类有共同的基类那么在继承关系中就会产生多个对共同基类的实例。通过使用虚继承可以确保在继承关系中只有一个对共同基类的实例。这样可以避免派生类中对共同基类的成员访问和命名冲突。虚继承会在派生类对象中引入虚基类指针Virtual Base Pointer和虚基类表Virtual Base Table用于管理共享基类的访问。 关系 虚函数和虚继承是C语言提供的两种不同的特性它们在语法和作用上是独立的。虚函数通过动态绑定实现运行时多态性而虚继承通过调整继承关系来解决多继承带来的问题。在某些情况下我们可能需要同时使用虚函数和虚继承。例如在存在多继承关系的类层次结构中如果基类中有虚函数并且派生类需要重写这些函数那么可以通过虚继承来消除多个对共同基类的实例同时实现动态多态性。 1️⃣纯虚函数 纯虚函数Pure Virtual Function是一种在基类中声明但不进行实现的虚函数。它通过在函数声明末尾添加 0 来标识例如 virtual void func() 0;。 纯虚函数在基类中起到以下作用 接口定义纯虚函数可以被视为基类对于派生类的接口定义定义了派生类必须实现的方法。强制继承类实现通过在基类中声明纯虚函数强制要求派生类必须提供实现确保每个派生类都具备相同的接口。抽象类包含一个或多个纯虚函数的类称为抽象类无法实例化对象。抽象类通常用作基类用于定义通用接口和行为。 派生类必须实现基类中的纯虚函数如果未能实现则派生类也成为了抽象类。只有当派生类实现了基类的所有纯虚函数时才能实例化派生类的对象。 以下是一个展示纯虚函数的代码示例 #includeiostream using namespace std;// 抽象基类 Animal class Animal { public:// 纯虚函数用于定义接口virtual void makeSound() 0; };// 派生类 Dog class Dog : public Animal { public:// 实现基类的纯虚函数void makeSound() override {cout 汪汪 endl;} };// 派生类 Cat class Cat : public Animal { public:// 实现基类的纯虚函数void makeSound() override {cout 喵喵 endl;} };int main() {Dog dog;Cat cat;dog.makeSound(); // 输出汪汪cat.makeSound(); // 输出喵喵return 0; } 【说明】 在上面的代码中Animal 是一个抽象基类其中声明了一个纯虚函数 makeSound()。而 Dog 和 Cat 是派生类它们必须实现 makeSound() 函数才能被实例化。通过上述代码我们可以看到通过纯虚函数的方式我们定义了一个通用的接口 makeSound()并要求派生类提供它们特定的实现。在 main() 函数中我们创建了 Dog 和 Cat 对象并调用它们的 makeSound() 函数分别输出对应的结果 总结来说纯虚函数是一种没有具体实现的函数用于定义基类的接口和要求派生类提供实现。 2️⃣ 面试题虚函数与纯虚函数的区别 首先给大家先抛出概念性的东西大家有个认识 1. 虚函数和纯虚函数可以定义在同一个类中含有纯虚函数的类被称为抽象类而只含有虚函数的类不能被称为抽象类。 2. 虚函数可以被直接使用也可以被子类重载以后以多态的形式调用而纯虚函数必须在子类中实现该函数才可以使用因为纯虚函数在基类有声明而没有定义。 3. 虚函数和纯虚函数都可以在子类中被重载以多态的形式被调用。 4. 虚函数和纯虚函数通常存在于抽象基类之中被继承的子类重载目的是提供一个统一的接口。 5. 虚函数的定义形式 virtual{} ; 纯虚函数的定义形式 virtual { } 0 ;6. 在虚函数和纯虚函数 的定义中不能有static标识符原因很简单被static修饰的函数在编译时要求前期绑定,然而虚函数却是动态绑定而且被两者修饰的函数生命周期也不一样。 举个虚函数的例子 class A { public:virtual void foo(){cout A::foo() is called endl;} }; class B :public A { public:void foo(){cout B::foo() is called endl;} }; int main(void) {A* a new B();a-foo(); // 在这里a虽然是指向A的指针但是被调用的函数(foo)却是B的!return 0; } 输出结果如下  【说明】 这个例子是虚函数的一个典型应用通过这个例子也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上一个类函数的调用并不是在编译时刻被确定的而是在运行时 刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数所以 被成为“虚”函数。 虚函数只能借助于指针或者引用来达到多态的效果。 纯虚函数是在基类中声明的虚函数它在基类中没有定义但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“0” virtual void funtion1()0 为了方便使用多态特性我们常常需要在基类中定义虚拟函数。 在很多情况下基类本身生成对象是不合情理的。例如动物作为一个基类可以派生出老虎、孔雀 等子类但动物本身生成对象明显不合常理。 为了解决上述问题引入了纯虚函数的概念将函数定义为纯虚函数方法 virtual ReturnType Function() 0; 则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类 称为抽象类它不能生成对象。这样就很好地解决了上述两个问题。 声明了纯虚函数的类是一个抽象类。所以用户不能创建类的实例只能创建它的派生类的实例。 纯虚函数最显著的特征是它们必须在继承类中重新声明函数不要后面的 0 否则该派生类也 不能实例化而且它们在抽象类中往往没有定义。 定义纯虚函数的目的在于使派生类仅仅只是继承函数的接口。 纯虚函数的意义让所有的类对象主要是派生类对象都可以执行纯虚函数的动作但类无法为 纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者 “ 你必须提 供一个纯虚函数的实现但我不知道你会怎样实现它 ” 。 3、虚函数的重写 概念 是指派生类中存在重新定义的函数。其函数名参数列表返回值类型所有都必须同基类中被重 写的函数一致。只有函数体不同花括号内派生类对象调用时会调用派生类的重写函数不会调用被重写函数重写的基类中被重写的函数必须有virtual修饰。 接下来我简单的用代码展示一下 #includeiostream using namespace std;class Person { public:virtual void BuyTicket() {cout 买票-全价 endl; } };class Student : public Person { public:virtual void BuyTicket() { cout 买票-半价 endl; } };void Func(Person p) {p.BuyTicket(); } int main() {Person ps;Student st;Func(ps);Func(st);return 0; }输出显示 【注意】 在重写基类虚函数时派生类的虚函数在不加virtual关键字时虽然也可以构成重写(因 为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范不建议这样使用。 1️⃣虚函数重写的两个例外 1. 协变 ( 基类与派生类虚函数返回值类型不同 ) 派生类重写基类虚函数时与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指 针或者引用派生类虚函数返回派生类对象的指针或者引用时称为协变。了解 例如以下代码  class a {}; class b : public a {};class person { public:virtual a* f() { cout new A endl;return nullptr;} }; class student : public person { public:virtual b* f() { cout new B endl;return nullptr;} };void Func(person* p) {p-f();delete p; }int main() {Func(new person);Func(new student);return 0; } 输出显示  而当我们想返回的是对象的时候此时编译器就会发生报错     【说明】 基类Person的虚函数f()返回类型是A*而派生类Student的重写函数f()的返回类型是B*这违反了上述规则因为B*不是A*的派生类。在C中如果要进行虚函数重写返回类型必须是完全匹配的或者是基类返回类型的派生类。在这种情况下你可以将 B* 转换为 A*然后在函数中返回一个派生类对象的指针。 2️⃣析构函数的重写(基类与派生类析构函数的名字不同) 首先我们先看这样的场景 class Person { public:~Person() {cout ~Person() endl;} }; class Student : public Person { public:~Student() { cout ~Student() endl; } };int main() {Person p;Student s;return 0; } 输出结果 经过我们的分析发现上述代码并没有问题。紧接着我把代码改动一下看最终的结果是什么 具体如下 【说明】 上述代码存在内存泄露问题。在代码中派生类 Student 是基类 Person 的子类并且在派生类中定义了析构函数。在 main() 函数中使用了动态内存分配来创建了两个对象 p1 和 p2分别指向 Person 类型和 Student 类型然而在释放这些动态分配的对象时只调用了 delete 关键字却没有使用虚析构函数。由于基类 Person 的析构函数不是虚函数因此在通过基类指针 p2 删除指向派生类对象的指针时将不会调用派生类 Student 的析构函数可能导致资源泄露。 为了解决这个问题只有派生类Student的析构函数重写了Person的析构函数下面的delete对象调用析构函 数才能构成多态才能保证p1和p2指向的对象正确的调用析构函数。 修正后的代码如下所示 【说明】 如果基类的析构函数为虚函数此时派生类析构函数只要定义无论是否加virtual关键字 都与基类的析构函数构成重写虽然基类与派生类析构函数名字不同虽然函数名不相同 看起来违背了重写的规则其实不然这里可以理解为编译器对析构函数的名称做了特殊处 理编译后析构函数的名称统一处理成destructor。 4、C11 override 和 final 从上面可以看出 C 对函数重写的要求比较严格但是有些情况下由于疏忽可能会导致函数 名字母次序写反而无法构成重载而这种错误在编译期间是不会报出的只有在程序运行时没有 得到预期结果才来 debug 会得不偿失因此 C11 提供了 override 和 final 两个关键字可以帮 助用户检测是否重写。 final修饰虚函数表示该虚函数不能再被重写 class Car { public:virtual void Drive() final {} }; class Benz :public Car { public:virtual void Drive() { cout Benz-舒适 endl; } };override: 检查派生类虚函数是否重写了基类某个虚函数如果没有重写编译报错。 class Car { public:void Drive(){} };class Benz :public Car { public:virtual void Drive() override {cout Benz-舒适 endl; } };int main() {Car cc;Benz bb;cc.Drive();bb.Drive();return 0; } 输出显示   因此想要达到相应的效果我们需要在基类中用虚函数实现 class Car { public:virtual void Drive(){} };class Benz :public Car { public:virtual void Drive() override {cout Benz-舒适 endl; } };int main() {Car cc;Benz bb;cc.Drive();bb.Drive();return 0; } 5、重载、覆盖(重写)、隐藏(重定义)的对比面试题 首先给大家一张表格让大家直观的感受 上述对于重写我已经实现。接下来我简单的实现一下剩下的两类 重载 我们在平时写代码中会用到几个函数但是他们的实现功能相同但是有些细节却不同函数重载是 指同一可访问区内被声明的几个具有不同参数列参数的类型个数顺序不同的同名函数根据参数列表确定调用哪个函数重载不关心函数返回类型。 代码演示 // 重载函数 void print(int num) {cout Integer: num endl; }void print(float num) {cout Float: num endl; }void print(const char* str) {cout String: str endl; }int main() {print(10); // 调用 print(int) 重载print(3.14f); // 调用 print(float) 重载print(Hello, World!); // 调用 print(const char*) 重载return 0; } 输出展示  隐藏(重定义) 在C中函数隐藏Function Hiding也称为函数重定义Function Redefinition指的是派生类中的函数隐藏了基类中的同名函数这种情况下无法使用基类指针或引用调用派生类中隐藏的函数。 以下是一个C代码示例来演示函数隐藏的情况 //函数隐藏 class Base { public:void print() {cout Base::print() endl;} };class Derived : public Base { public:void print() {cout Derived::print() endl;} };int main() {Base base;Derived derived;base.print(); // 调用基类 Base 的 print()derived.print(); // 调用派生类 Derived 的 print()// 使用基类指针或引用调用派生类中隐藏的函数Base* basePtr derived;basePtr-print(); // 调用基类 Base 的 print()派生类的函数被隐藏return 0; } 输出演示 【说明】 在上述示例中我们创建了一个基类 Base 和一个派生类 Derived。两个类中都定义了名为 print() 的函数其中派生类 Derived 的 print() 函数隐藏了基类 Base 中的同名函数。在 main() 函数中我们分别创建了一个基类对象 base 和派生类对象 derived。然后通过调用 base.print() 和 derived.print()可以分别看到基类和派生类中的 print() 函数的输出结果。接下来我们使用基类指针 basePtr 指向派生类对象 derived然后通过 basePtr-print() 调用 print() 函数。这时会发现调用的是基类 Base 中的 print() 函数而派生类中的函数被隐藏而不可访问。 三抽象类 1、概念 在虚函数的后面写上 0 则这个函数为纯虚函数。 包含纯虚函数的类叫做抽象类也叫接口 类抽象类不能实例化出对象 。派生类继承后也不能实例化出对象只有重写纯虚函数派生 类才能实例化出对象。纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承。 例如以下代码 class Car { public: virtual void Drive() 0; }; class Benz :public Car { public:virtual void Drive(){cout Benz-舒适 endl;} }; class BMW :public Car { public:virtual void Drive(){cout BMW-操控 endl;} }; void Test() { Car* pBenz new Benz;pBenz-Drive();Car* pBMW new BMW;pBMW-Drive(); } 输出显示 2、接口继承和实现继承 普通函数的继承是一种实现继承派生类继承了基类函数可以使用函数继承的是函数的实 现。虚函数的继承是一种接口继承派生类继承的是基类虚函数的接口目的是为了重写达成多态继承的是接口。所以如果不实现多态不要把函数定义成虚函数。 四多态的原理 1、虚函数表 首先这里常考一道笔试题sizeof(Base)是多少 class Base { public:virtual void Func1(){cout Func1() endl;} private:int _b 1;char _ch; };int main() {Base b;cout sizeof(Base) endl;return 0; } 输出显示 【说明】 通过观察测试我们发现 b 对象是 8bytes 除了 _b 成员还多一个 __vfptr 放在对象的前面 ( 注意有些 平台可能会放到对象的最后面这个跟平台有关 ) 对象中的这个指针我们叫做 虚函数表指针 (v 代 表 virtual f 代表 function) 一个含有虚函数的类中都至少都有一个虚函数表指针因为虚函数 的地址要被放到虚函数表中虚函数表也简称虚表。 那么派生类中这个表放了些什么呢我们接着往下分析 // 针对上面的代码我们做出以下改造 1.我们增加一个派生类Derive去继承Base 2.Derive中重写Func1 3.Base再增加一个虚函数Func2和一个普通函数Func3 class Base { public:virtual void Func1(){cout Base::Func1() endl;}virtual void Func2(){cout Base::Func2() endl;}void Func3(){cout Base::Func3() endl;} private:int _b 1; };class Derive : public Base { public:virtual void Func1(){cout Derive::Func1() endl;} private:int _d 2; };int main() {Base b;Derive d;return 0; } 通过调试我们可以得到以下这正图片 【说明】   1. 派生类对象 d 中也有一个虚表指针 d 对象由两部分构成一部分是父类继承下来的成员虚表指针也就是存在部分的另一部分是自己的成员。 2. 基类 b 对象和派生类 d 对象虚表是不一样的这里我们发现 Func1 完成了重写所以 d 的虚表 中存的是重写的 Derive::Func1 所以虚函数的重写也叫作覆盖 覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法覆盖是原理层的叫法。 3. 另外 Func2 继承下来后是虚函数所以放进了虚表 Func3 也继承下来了但是不是虚函 数所以不会放进虚表。 4. 虚函数表本质是一个存虚函数指针的指针数组一般情况这个数组最后面放了一个 nullptr 。 5. 总结一下派生类的虚表生成 a.先将基类中的虚表内容拷贝一份到派生类虚表中b.如果派生类重写了基类中某个虚函数用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。 6. 这里还有一个童鞋们很容易混淆的问题 虚函数存在哪的虚表存在哪的 虚函数存在虚表虚表存在对象中。注意上面的回答的错的。但是很多童鞋都是这样深以为然的。注意虚表存的是虚函数指针不是虚函数虚函数和普通函数一样的都是存在代码段的只是他的指针又存到了虚表中。另外对象中存的不是虚表存的是虚表指针。 2、多态的原理 上面分析了这个半天了那么多态的原理到底是什么还记得这里 Func 函数传 Person 调用的 Person::BuyTicket 传 Student 调用的是 Student::BuyTicket class Person { public:virtual void BuyTicket() {cout 买票-全价 endl; } };class Student : public Person { public:virtual void BuyTicket() {cout 买票-半价 endl;} };void Func(Person p) {p.BuyTicket(); }int main() {Person mike;Func(mike);Student john;Func(john);return 0; } 接下来我们对代码进行调试观察 【说明】 1. 观察上图的红色箭头我们看到p是指向mike对象时p-BuyTicket在mike的虚表中找到虚 函数是Person::BuyTicket。 2. 观察下图的蓝色箭头我们看到p是指向johnson对象时p-BuyTicket在johson的虚表中 找到虚函数是Student::BuyTicket。 3. 这样就实现出了不同对象去完成同一行为时展现出不同的形态。 4. 反过来思考我们要达到多态有两个条件一个是虚函数覆盖一个是对象的指针或引用调 用虚函数。反思一下为什么 5. 再通过下面的汇编代码分析看出满足多态以后的函数调用不是在编译时确定的是运行 起来以后到对象的中取找的。不满足多态的函数调用时编译时确认好的。 其次我在带大家看下底层的汇编指令看多态状态和不是多态状态下的场景 3、动态绑定与静态绑定 由于 派生类重写基类方法然后用基类引用指向派生类对象调用方法时候会进行动态绑定这就是多 态 。 多态分为 静态多态和动态多态 静态多态 编译器在编译期间完成的编译器会根据实参类型来推断该调用哪个函数 如果有对应 的函数就调用没有则在编译时报错。 以下是一个示例展示了如何在 C 中实现静态绑定 class Base { public:void display() {cout 买票-全价 endl;} };class Derived : public Base { public:void display() {cout 买票-半价 endl;} };int main() {Base baseObj;Derived derivedObj;baseObj.display(); // 静态绑定调用 Base 类的 display() 函数derivedObj.display(); // 静态绑定调用 Derived 类的 display() 函数return 0; } 输出显示 【说明】 由于函数调用使用的是静态绑定编译器在编译时就知道要调用哪个函数。因此baseObj.display() 调用了 Base 类的 display() 函数而 derivedObj.display() 调用了 Derived 类的 display() 函数。 这就是 C 中如何实现静态绑定的方式。可以看出在没有使用虚函数或基类指针/引用的情况下默认使用的是静态绑定。 动态多态 其实要实现动态多态需要几个条件 —— 即动态绑定条件 1. 虚函数。基类中必须有虚函数在派生类中必须重写虚函数。 2. 通过基类类型的指针或引用来调用虚函数。 class Base { public:virtual void display() {cout Base::display() endl;} };class Derived : public Base { public:void display() override // 使用 override 关键字指明这是一个重写的虚函数{ cout Derived::display() endl;} };int main() {Base baseObj;Derived derivedObj;Base* ptr1 baseObj; // 基类指针指向基类对象Base* ptr2 derivedObj; // 基类指针指向派生类对象ptr1-display(); // 动态绑定调用 Base 类的 display() 函数ptr2-display(); // 动态绑定调用 Derived 类的 display() 函数return 0; } 输出展示 【说明】 由于函数调用使用的是动态绑定当我们通过指针调用 display() 函数时实际调用的函数版本根据指针指向的对象类型来确定因此ptr1-display() 调用了基类 Base 的 display() 函数而 ptr2-display() 调用了派生类 Derived 的 display() 函数。 这就是在 C 中实现动态绑定的方式。通过使用虚函数和基类指针/引用我们能够在运行时根据对象的实际类型确定要调用的函数版本。 最后给大家推荐一篇文章帮助大家更好的理解: C 虚函数表解析 总结 到此关于多态相关的知识便讲解结束了。感谢大家的观看与支持
http://www.yingshimen.cn/news/34780/

相关文章:

  • 怎么查网站备案号做分析图网站
  • 宁波seo整站优化便宜高端网站设计推荐
  • 城市网站建设意义专业技能培训机构
  • 做网站卖什么发财湖北政务服务网
  • 天猫网站网址企业网站开发时间
  • gta5买房网站正在建设做便民网站都需要提供什么
  • 佛山微信网站设计seo优化网站百度技术
  • 深圳大腕互联网站建设温州网站建设小程序
  • 建设门户网站的意见和建议给艺术家做网站的工作
  • 做网站属于印花税哪个范畴天津猎头公司
  • 设计图片免费素材网站名城苏州网首页
  • 1150网站建设服务器主板360建站工具
  • 门户型网站都有哪些做app和网站
  • 公司网站未备案智能建站系统开发
  • 校园网站平台建设推广营销策划
  • 做网站有哪些流程网络优化工作应该怎么做
  • 株洲专业网站建设品牌gif8.net基于wordpress
  • 营销型的物流网站什么网站可以做单词书
  • 个人网站开发用什么语言天津网站建设 熊掌号
  • 通辽建设网站招聘网站入职分析表怎么做
  • 网站的百度百科怎么做网站建设APP的软件
  • 网站分站的实现方法网站后台编辑怎么做
  • 网站制作详细流程广东搜索引擎优化
  • 学校网站建设评分标准山东省建设厅网站首页
  • 无水印视频素材下载免费网站国家企业公示网入口官网登录
  • 吉利汽车网站开发环境分析萧山seo
  • 网站建设改版公司专做农产品的网站有哪些
  • 银川哪家网络公司做网站做得好网页制造基础课程
  • 网站页面网络维护合同
  • 做团购网站视频做二维码签到的网站