收藏 分销(赏)

第9章 模板.ppt

上传人:xrp****65 文档编号:13337327 上传时间:2026-03-03 格式:PPT 页数:45 大小:589.50KB 下载积分:10 金币
下载 相关 举报
第9章 模板.ppt_第1页
第1页 / 共45页
第9章 模板.ppt_第2页
第2页 / 共45页


点击查看更多>>
资源描述
单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,单击此处编辑母版标题样式,第,9,章 模板,主要内容,1,2,3,4,5,9.1,函数模板,9.2,类模板,9.3,模板编译模式,9.4,模板和代码复用,9.5,小结,9.1,函数模板,C+,是一种强类型的语言,这对实现某些简单的函数似乎是个障碍。例如下面的,max(),函数,虽然算法很简单,但,C+,要求我们为所有要比较的类型都提供一个实现:,int,max(,int,a,int,b),return a b?a:b;,double max(double a,double b),return a b?a:b;,这要求对不同的数据类型重复书写几乎相同的代码。虽然“,Copy&Paste,”,的编辑功能可以减少我们的工作量,但是会造成代码的重复和冗余。而且这种方法还有一个缺点:如果算法本身有所改变,那么对处理所有数据类型的算法代码都要一一进行修改。,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,9.1,函数模板,一种替代方法是用预处理器宏,例如:,#define,max(a,b)(a)(b)?(,a):(b,),这个宏定义虽然对简单的,max(),调用能够正常工作,但宏调用只是简单的参数替换,和函数调用机制不同,在表达式,尤其是有副作用的表达式作参数的情况下,会产生不正确的结果。,函数模板提供了一种描述通用算法的机制,它将算法处理的数据类型参数化,从而可以保留函数定义和函数调用的语义,又不必绕过,C+,的强类型检查。,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,9.1.1,函数模板的定义,函数模板提供了一个用来自动生成各种类型函数实例的算法。程序员对于函数接口(参数和返回类型)中的全部或者部分类型进行参数化,而函数体保持不变。如果一个函数的实现对一组实例都是相同的,并且每个实例都处理唯一的一种数据类型,例如,max(),,那么该函数就可以定义为函数模板。,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,max(),函数模板的定义,下面是,max(),函数模板的定义:,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,template,Type max(Type a,Type b),return a b?a:b;,模板参数表,模板参数,模板参数,:,模板类型参数,它代表了一种类型;模板非类型参数,它代表一个常量表达式。,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,template,Type min2(const Type(&,array)size,),/,传递数组的,const,引用,Type,min_val,=array0;,for(,int,ix=1;ix size;+ix),if(,arrayix,int,int,minia,=min1(ia,6);,/,隐式实例化:,Type=double,double,minda,=min1(da,5);,/,隐式实例化:,Type=,int,size=6,int,mi=min(,ia,);,/,隐式实例化:,Type=double,size=5,double,md,=min(,da,);,编译器对函数模板进行实例化,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,函数模板本身并不是完整的函数定义,因为其中的类型信息不完整。函数模板被调用时编译器对其进行实例化,生成真正完整的函数定义,再实施对生成函数的调用。根据模板调用是的实参不同,一个函数模板可以实例化得到多个不同的函数定义。,实例化过程,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,函数模板调用,min1(ia,6);,函数模板定义,Template,Type,min1(Type array,int,size),第一步:模板参数实例化,Type=,int,函数模板调用,min1(da,5);,函数实例,double min1(double array,int,size),函数实例,int,min1(int array,int,size),第二步:,函数调用绑定,Type=double,模板实参推演,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,为了判断用作模板实参的实际类型和值,编译器需要检查函数调用中提供的函数实参的类型。用函数实参的类型来决定模板实参的类型和值的过程被称为,模板实参推演,。,double,minda,=min1(da,5);/,da,的类型是,double:Type=double,int,mi=min(,ia,);/,ia,的类型是,int,,大小是,6:Type=,int,size,=6,显式指定模板实参,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,在使用函数模板时,必须能够通过上下文为一个模板实参确定一个唯一的类型或值,否则会产生编译错误。,template,T1 func(T2 arg1,T3 arg2)/*/,template,void,goo(T,a,T b)/*/,void main(),int,x,y;,double d;,x=,func,(y,d);/Error:,goo(x,d);/Error:,二义性:,T,是,int,还是,double,?,解决这种问题的方法是显式指定模板实参:,x=,func,(,y,d,);/OK,显式指定,x=,func,(y,d);/OK,,能够推演出,T2 T3,goo,(d,y);/OK,对,y,进行类型转换,9.1.3,函数模板的重载,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,函数模板可以像一般函数一样重载。例如:,template,Type max(Type a,Type b),return a b?a:b;,template,Type max(Type a,Type b,Type c),Type t=a b?a:b;,return t c?t:c;,通常是参数的个数不同,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,函数模板的重载通常是参数的个数不同,而不是参数类型。例如,下面的两个声明将指同一个函数模板:,template,Type max(T1 a,T1 b),return a b?a:b;,template,Type max(T2 a,T2 b),return a b?a:b;,非模板函数重载同名的函数模板,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,经常使用的还有另一种重载方式:用一个非模板函数重载一个同名的函数模板。例如,用上面的,max(),函数模板比较两个,C,风格字符串:,char*s1=“,abc,”;,char*s2=“def”;,max(s1,s2);,为此,我们可以写一个专门处理字符串比较的非模板函数,对,max(),模板进行重载。,char*max(char*a,char*b),return(,strcmp(a,b)0)?a:b;,这样,当参数的类型为,char*,时,会调用这个非模板函数进行比较。,普通函数,/,模板函数,:,重载解析步骤,2,类模板,3,模板编译模式,4,模板和代码复用,5,小结,函数模板,1,函数模板的定义,函数模板的实例化,函数模板的重载,对于一个函数调用,考虑普通函数和模板函数的函数重载解析步骤如下:,寻找一个参数完全匹配的非模板函数,若找到,则调用该函数;,寻找一个函数模板,将其实例化为一个完全匹配的函数,若找到,则调用;,只考虑重载函数集中的普通函数,按照第,4,章的重载解析过程选择可调用的函数,若找到,则调用。,在上面三步中如果都没有找到匹配的函数,则出现无可调用函数的错误。如果某一步出现了多个匹配的函数,则引起二义性错误。,9.2,类模板,假设我们想定义一个类来支持队列的机制。队列是一种用于对象集合的数据结构,对象被加入到队列的尾部,而从队列的顶部被删除,称为先进先出(,FIFO,)。我们设计的队列支持下面的操作:,在队列尾加入一个元素;,从队列首删除一个元素;,判断队列是否为空;,判断队列是否已满。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,问题,这个队列类可能如下定义:,class Queue,public:,Queue();,Queue();,Type/,删除元素,void,add(const,Type/,加入元素,bool,is_empty,();/,判断是否空,bool,is_full,();/,判断是否满,;,现在的问题是,对于,Type,,我们应该使用什么类型?如果我们选择,int,来代替,Type,,那么这个,Queue,类就只能处理,int,型的对象。如果想用队列处理另一种类型的数据,例如,double,、,string,或者用户自定义的对象,应该怎么办?,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,一种解决方法,一种解决方法是拷贝代码,拷贝整个,Queue,类的实现,并修改它,使之能够对,double,类型工作,然后对下一个类型再拷贝,再修改。而且由于类名字不能重载,必须为每个队列类指定不同的名字:,IntQueue,、,DoubleQueue,、,StringQueue,,等等。每当需要一个新类时,我们重复这个拷贝、修改和重命名的过程。这种过程不仅是无休止的,而且会引起复杂的维护问题。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,C+,中用类模板来定义通用类型,可以定义队列类模板,利用模板机制为每个具体类型的队列自动生成相应的类定义。,Queue,类模板可以如下定义:,template,class Queue,public:,Queue();,Queue();,Type,void,add(const,Type,bool,is_empty,();,bool,is_full,();,;,使用下面的代码可以生成各种类型的队列:,Queue,qi,;/,int,队列,Queue,qd,;/double,队列,Queue,qs,;/string,队列,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,9.2.1,类模板的定义,类模板的定义和声明都以关键字,template,开头,,,template,之后是尖括号括起来的,模板参数表,。模板参数可以是模板类型参数,也可以是模板非类型参数。,模板类型参数,由关键字,class,或,typename,后加一个标识符构成。,class,和,typename,这两个关键字在模板参数表中的意义是相同的。,一个类模板可以有,多个类型参数,,每个类型参数前都要有,class,或,typename,关键字。,模板非类型参数,由一个普通的参数声明构成。模板非类型参数代表了一个潜在的值,这个值是模板定义中的一个常量。在模板被实例化时,非类型参数会被一个编译时刻已知的常量值代替。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,类模板,Queue,的定义,/queue.h,template class,QueueItem,public:,QueueItem(const,T,private:T item;,QueueItem,*next;,;,template class Queue,public:,Queue():front(0),back(0);,Queue();,Type,void,add(const,Type,bool,is_empty()const,return front=0;,private:,QueueItem,*front;,QueueItem,*back;,;,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.2.2,类模板的实例化,类模板定义本身并不定义任何类,而只是指定了怎样根据一个或多个实际的类型或值来构造单独的类。从通用的类模板定义生成类的过程被称为模板实例化。例如:,Queue,qi,;/Type,用,int,代替,Queue,qs,;/Type,用,string,代替,这两条语句分别将,Queue,类模板实例化为两个类:一个是将类模板中的参数,Type,用,int,代替,另一个是将,Type,用,string,代替。,qi,和,qs,都是类类型的对象,但是分属于,Queue,和,Queue,两个不同的类型。,同一个类模板用不同的类型实例化后得到的类之间并没有任何特殊的关系,类模板的每个实例都是一个独立的类型。,类模板在实例化时必须显式指定模板实参。声明一个类模板实例的指针和引用不会引起类模板的实例化。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.2.3,类模板的成员函数,和非模板类一样,类模板的成员函数也可以在类模板的定义中定义,这时,该成员函数是,inline,成员函数,例如,Queue,模板类中的,is_empyt,(),成员函数。,成员函数也可以定义在类模板定义之外,这时要使用特殊的语法,以指明它是一个类模板的成员。成员函数定义的前面必须加上关键字,template,以及模板参数。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,成员函数类外定义,Queue,类模板的析构函数和,add(),的类外定义如下:,template Queue:Queue(),while(!,is_empty,(),remove();,template,void Queue:,add(const,Type&,val,),QueueItem,*pt=new,QueueItem,(,val,);,if(,is_empty,(),front=back=pt;,else,back-next=pt;,back=pt;,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.2.4,类模板的非类型参数,模板的非类型参数代表一个常量值,大多数情况下都是整型。如果使用模板非类型参数表示堆栈的大小,,Stack,类模板也可以如下定义:,template /cap,在,Stack,定义中用作常量,class Stack,public:,Stack();/,构造函数不带,int,参数,由模板参数,cap,提供堆栈大小,Stack()delete,ele,;,void,push(Type,e),ele+top,=e;,Type pop()return,eletop,-;,bool,empty()return top=bottom;,bool,full()return top=size-1;,private:,Type*,ele,;,int,top;,int,size;,const static,int,bottom=-1;,;,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.2.4,类模板的非类型参数,/成员函数的类外定义,两个模板参数,template Stack:Stack(),assert(cap 0);,size=cap;,ele=new Typesize;,top=bottom;,/-,/使用Stack 类模板的代码,int main(),Stack is;/实例化时指定两个参数:Type=int,cap=10,for(int i=0;i10;i+)is.push(i);,while(!is.empty()coutis.pop()t;,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.2.5,类模板的静态数据成员,类模板可以声明静态数据成员,静态数据成员的定义必须出现在类模板定义之外。,类模板的每个实例都有自己的一组静态数据成员。每个静态数据成员实例都与一个类模板实例相对应。因此,一个静态数据成员的实例总是通过一个特定的类模板实例被引用。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.2.6,类模板中的友元,三种友元声明可出现在类模板中:,非模板友元类或友元函数。不使用模板参数的友元类或友元函数是类模板的所有实例的友元。,绑定的友元类模板或函数模板。使用模板类的模板参数的友元类和友元函数与实例化后的模板类实例之间是一一对应的关系。,非绑定的友元类模板或函数模板。这时友元类模板或函数模板有自己的模板参数,因此和模板类实例之间形成一对多的映射关系,即对任一个模板类实例,友元类模板或函数模板的所有实例都是它的友元。,C+,标准化之前的编译器不支持这种友元声明。,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,例如,template,class MTC,public:,friend void,foo,();,/,foo,(),是所有,MTC,模板的实例的友元,friend void,goo,(,vect,v);,/,每个实例化的,MTC,类都有一个相应的,goo,(),友元实例,template,friend void,hoo,(MTC);,/,对,MTC,的每个实例,,hoo,(),的每个实例都是它的友元,;,3,模板编译模式,4,模板和代码复用,5,小结,1,函数模板,类模板,2,类模板的定义,类模板的实例化,类模板的成员函数,类模板的非类型参数,类模板的静态数据成员,类模板中的友元,9.3,模板编译模式,函数模板本身并不定义任何函数,它只是一组函数实例定义的规范描述。同样,类模板也只是一组类类型的定义的规范描述,类模板定义本身并没有定义任何类类型。只有编译器看到模板真正被使用时才实例化该模板。函数模板和类模板都可以被实例化多次,在实例化模板时,要求有模板的完整定义。那么模板定义的代码应该如何组织呢?,C+,模板编译模式指定了定义和使用模板的程序代码组织方式。,C+,支持两种模板编译模式:,包含模式,和,分离模式,。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,9.3.1,包含编译模式,在包含编译模式下,模板被实例化的文件中要包含模板的完整定义。对函数模板而言,要在它被实例化的每个文件中都包含其定义;对类模板而言,类模板的定义和所有成员函数以及静态数据成员的定义也必须完全被包含在每个要将它们实例化的文件中。所以,一般将函数模板的定义,类模板的完整定义(包括在类模板外定义的成员函数)都放在头文件中,在使用模板的文件中包含相应的头文件。,注意,为了避免因为模板定义被多次包含引起的编译错误,在模板定义的头文件中应该使用包含守卫:,#,ifndef/#define/#endif,。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,templatemin.h,/,模板定义头文件,templatemin.h,#,ifndef,TEMPLATE_MIN_H,#define TEMPLATE_MIN_H,template,Type,min(Type,array,int,size),Type,min_val,=array0;,for(,int,ix=1;ix size;+ix),if(,arrayix,min_val,),min_val,=,arrayix,;,return,min_val,;,#,endif,/TEMPLATE_MIN_H,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,test.cpp,/,使用模板定义的源文件,test.cpp,include,#include,templatefunc.h,using namespace std;,int,main(),int,a=2,5,3,4,1,7;,cout,min(a,6),endl,;,double,da,=1.2,3,5,7.2;,cout,min(da,4),endl,;,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,包含编译模式两个缺点,这种组织方式有两个缺点:,函数体中描述的是实现细节,如果将函数定义和成员函数的定义都放在头文件中,就不能隐藏这些细节。,在多个文件中包含相同的函数模板定义和类模板定义会增加不必要的编译时间。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,9.3.2,分离编译模式,分离编译模式允许我们将类模板的接口与它的实现分离。在分离编译模式下,,函数模板的声明,放在头文件中,,定义,放在源文件中;,类模板的声明,放在头文件中,而,成员函数和静态数据成员的定义,被放在源文件中。在这种模式下,模板代码的组织方式,与非模板代码的组织方式相同。只不过,,我们要用,export,关键字将模板声明为可导出的,。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,分离编译模式例子,/,smodel.h,/,只提供函数模板的声明,template,Type,max(Type,x,Type y);,/,smodel.cpp,/,提供函数模板的定义,export template,Type,max(Type,x,Type y)/*/,/,客户代码中只需要包含头文件就可使用模板了,#include“,smodel.h,”,min(a,b);,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,可导出的类模板,可导出的类模板在声明或定义时在,template,关键字前加上,export,:,/queue.h,export template,class Queue;,/,queue.cpp,#include“queue.h”,/,模板类成员函数的定义,在模板的实现文件(,queue.cpp,)中,成员函数的定义也自动被声明为可导出的。在其他文件使用这些成员函数的实例之前,这些成员的定义可以不出现。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,类模板的部分成员声明为可导出,也可以将类模板的部分成员声明为可导出的,这时关键字,export,不是在类模板上指定,而是在导出的成员函数定义上指定。那么那些没有指定,export,的成员定义也必须和类定义一起放在头文件中。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,可导出的类模板,/queue.h,template,class Queue,public:,Type,void add(const Type,;,template,Type&Queue:remove(),/,queue.cpp,#include“queue.h”,/add(),是可导出的成员函数,export template,void Queue:,add(const,Type&,val,),4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,大部分,C+,编译器不支持,分离编译模式虽然使我们能够将函数模板的接口同其实现分离,但是,分离编译模式需要更复杂的程序设计环境,所以它们不能在所有的,C+,编译器实现中提供。目前大部分,C+,编译器都不支持分离编译模式,如,GNU C+,和,Microsoft Visual C+,,都采用了忽略,export,关键字的策略。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,编写模板代码时采用的步骤,实际编程时,模板语法容易引起编译错误。为了使代码调试更容易,在编写模板代码时通常采用以下步骤:,编写对某个类型适用的普通函数或普通类,编译、运行、测试,确定代码可以正确工作之后进入下一步;,使用模板语法改写代码,用包含编译模式调试模板代码。注意模板定义头文件中要使用包含守卫;,如果编译器支持,export,,再将代码改写为分离编译模式进行调试。,4,模板和代码复用,5,小结,1,函数模板,2,类模板,模板编译模式,3,包含编译模式,分离编译模式,9.4,模板和代码复用,模板机制支持通用型程序设计,也是一种复用代码的机制,与继承和组合相比,模板的使用限制更多,灵活性不足,因而适用范围有限。,与继承类似,模板描述的也是共性。只不过继承描述类型在概念上的相似性,而模板要求代码的相似度。模板对代码的相似度要求非常严苛:除了算法处理的数据类型不同之外,其余代码应该完全相同。,继承描述一组具有公共接口的类之间的层次关系。类层次中各个类之间的差异往往体现在对消息的不同处理方式上。类模板在实例化之后形成真正的类型定义。实例化同一个类模板得到的各种类型之间除了代码相似之外,没有任何内在的逻辑关系,这些类可以分别实例化各自的对象,同样,这些对象也是互不相干。类模板的不同实例之间的差异体现在代码中的某些数据类型和常量值不同。,继承利用虚函数的运行时绑定展现包含多态性,是动态机制。模板则通过类型参数化实现了参数多态性,模板实例化发生在编译期间,是静态机制。,5,小结,1,函数模板,2,类模板,3,模板编译模式,模板和代码复用,4,9.5,小结,模板是重用代码的一种机制,利用模板可以进行与类型无关的通用型程序设计。,函数模板可以实现通用算法。函数模板的实例化只在函数被调用的时候发生。,类模板可以定义通用的数据结构。类模板的实例化只在创建对象时发生,声明类模板实例对象的指针或引用不能引起类模板的实例化。,目前的大多数,C+,编译器只支持模板的包含编译模式,组织代码时需要注意。,1,函数模板,2,类模板,3,模板编译模式,4,模板和代码复用,小结,5,Thank You!,
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 百科休闲 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服