C/C++培训
达内IT学院
400-996-5531
1、 c++中的空类, 默认产生哪些类成员函数?
默认构造函数,拷贝构造函数,析构函数,赋值运算符(operator =),取址运算符(operator &)(一对,一个非const,一个const)。
classEmpty{
public:
Empty();//缺省构造函数
Empty(constEmpty&);//拷贝构造函数
~Empty();//析构函数
Empty&operator=(constEmpty&);//赋值运算符
Empty*operator&();//取址运算符
constEmpty*operator&()const;//取址运算符const
}
2、 单例模式的特点是什么?用c++实现单例模式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问。
注意:
(1)单例类只能有一个实例
(2)单例类必须自己创建自己的唯一实例
(3)单例类必须给所有其他对象提供这一实例
单例模式需满足两个条件:
(1)保证一个类只创建一个实例
(2)提供一个对该实例的全局访问点
如果系统有类似的实体(有且只有一个,且需要全局访问),那么就可以将其实现为一个单例。实际工作中常见的应用举例:
(1)日志类:一个应用往往只对应一个日志实例
(2)配置类:应用的配置集中管理,比提供全局访问
(3)管理器:比如windows系统的任务管理器就是一个例子,总是只有一个管理器的实例
(4)共享资源类:加载资源需要较长时间,使用单例可以避免重复加载资源,并被多个地方共享访问。
c++实现单例模式
(1)懒汉模式
singleton在程序第一次调用的时候才会初始化
classSingleton{
private:
staticSingleton*instance;
Singleton(){};
public:
staticSingleton*GetInstance(){
if(NULL==instance){
instance=newSingleton();
}
returninstance;
}
};
Singleton*Singleton::instance=NULL;
//静态数据成员是静态存储的,必须对它进行初始化
使用该模式时,由于if语句的存在,会影响调用的效率。而且,在多线程环境下使用时,为了保证只能初始化一个实例,需要用锁来保证线程安全性,防止同时多个线程进入if语句中。
加入double-check,代码变为:
classSingleton{
private:
staticSingleton*instance;
Singleton(){};
public:
staticSingleton*GetInstance(){
if(NULL==instance){
Lock();//借用其他类来实现,如boost
if(NULL==instance){
instance=newSingleton();
}
Unlock();
}
returninstance;
}
};
Singleton*Singleton::instance=NULL;
Lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待。(即被阻止),直到该对象被释放。
如果处理大量数据时,锁会成为整个性能的瓶颈。
一般懒汉模式适用于程序一部分中需要使用Singleton,且在实例化后没有大量频繁访问或线程访问的情况。
(2)饿汉模式
无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。
由静态初始化实例保证其线程安全性。因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化。不必担心多线程问题。
故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。
classSingleton{
private:
staticconstSingleton*instance;
Singleton(){};
public:
staticconstSingleton*GetInstance(){
returninstance;
}
};
constSingleton*Singleton::instance=newSingleton();
静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类。
之前的处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。
3、 c++中不同数据类型所占用的内存大小?
单位都为字节 32位机器 64位机器
char 1 1
int 4 大多数是4,少数是8
short 2 2
long 4 8
float 4 4
double 8 8
指针 4 8
4、 编写类String的构造函数、 析构函数和赋值函数。
已知类String的原型为:
classString{
public:
String(constchar*str=NULL);//普通构造函数
String(constString&other);//拷贝构造函数
~String(void);//析构函数
String&operator=(constString&other);//赋值函数
private:
char*m_data;//用于保存字符串
}
解答:
//普通构造函数
String::String(constchar*str=NULL){
if(str==NULL){
m_data=newchar[1];
//得分点,对空字符串自动申请存放结束标志'\0'
*m_data='\0';
}else{
intlength=strlen(str);
m_data=newchar[length+1];
strcpy(m_data,str);
}
}
//String的析构函数
String::~String(void){
delete[]m_data;
}
//拷贝构造函数
String::String(constString&other){
//得分点,输入参数为const型
intlength=strlen(other.m_data);
m_data=newchar[length+1];//加分点,对other.m_data做NULL判断
strcpy(m_data,other.m_data);
}
//赋值函数
String&String::operator=(constString&other){
if(this==&other){
return*this;
}
delete[]m_data;//得分点,释放原有的内存资源
intlength=strlen(other.m_data);
m_data=newchar[length+1];//加分点,对m_data加NULL判断
strcpy(m_data,other.m_data);
return*this;//得分点,返回本对象的引用
}
5、 引用和指针有什么区别?
本质上:引用是别名,指针是地址
具体的:
(1)引用在创建的同时必须被初始化,不能有NULL引用,引用只能在定义时被初始化一次,之后不可变。指针可以在运行时改变其指向的值。
(2)从内存上看,指针是一个实体,指针会分配内存区域。而引用不会,它仅仅是一个别名。
(3)在参数传递时,引用会做类型检查,而指针不会,所以引用是安全的。
(4)引用不能为空,指针可以为空。
(5)引用没有const,指针有const,const的指针不可变。
(6)“sizeof引用”得到的是所指向的变量(对象)的大小,而“sizeof指针”得到的是指针本身的大小。指针和引用的自增(++)运算意义不一样。
例子:
inta=0;
int&b=a;//b是a的一个引用,b只是a的一个别名,和a一样使用。
int*p=&a;//p是a的一个指针
cout<<b<<endl;
b++;
cout<<b<<endl;
cout<<p<<endl;
p++;
cout<<p<<endl;
输出:
0
1
0x7fff59fdeb98
0x7fff59fdeb9c
关于引用:
引用:就是某一个变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符 &引用名 = 目标变量名
例如引用a:
int a;
int &ra = a;
注意:
(1)&在此不是求地址运算符,而是起标识作用
(2)类型标识符是指目标变量的类型
(3)声明引用时,必须同时对其进行初始化
(4)引用声明完毕后,相当于目标变量有两个名称即该目标原名称和引用名,且不能再把该引用作为其他变量的别名。
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址,&ra与&a相等。
(6)不能建立数组的引用,因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
6、 c++如何连接数据库?
连接数据库大致分如下四个步骤:
(1)初始化
(2)用Connection对象连接数据库
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理
(4)使用完毕后关闭连接释放对象
7、 c++模板
模板是c++支持参数多态化的工具,使用模板可以使用类或者函数声明一种一般模式,使得类中的某些数据成员或成员函数的参数,返回值取得任意类型。
模板是一种对类型进行参数化的工具。
通常有两种形式:函数模板和类模板
(1)函数模板针对仅参数类型不同的函数
(2)类模板针对仅数据成员和成员函数类型不同的类
使用模板的目的就是能够让程序员编写与类型无关的代码。
例如编写了一个交换两个整型int类型的swap函数,这个函数就只能实现int型,对double、字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,既可以实现int型,又可以实现double型的交换,模板可以应用于函数和类。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行,即不能在局部范围,函数内进行,比如不能在main函数中声明或定义了一个模板。
函数模板通式:
函数模板的格式
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
函数体
}
其中template和class是关键字,class可以用typename 关见字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为
template <class T> void swap(T& a, T& b){},
当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中a和b是int 型,这时模板函数swap中的形参T就会被int所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。
注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
8、 c++中new、 malloc的区别?
(1)申请的内存所在位置
ew操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
自由存储区是c++基于new操作符的一个抽象概念。自由存储区可以是堆,也可以是静态存储区,这都看operator new在哪里为对象分配内存。
(2)返回类型安全性
ew操作符内存分配成功时,返回的是对象类型的指针。类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。malloc内存分配成功则是返回void ,需要通过强制类型转换将void 指针转换成我们需要的类型。
(3)内存分配失败时的返回值
ew内存分配失败时,会抛出bac_alloc异常,它不会返回NULL,malloc分配内存失败时返回NULL。
(4)是否需要指定内存大小
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。
malloc则需要显式地指出所需的尺寸。
例子:
A *ptr = new A;
A ptr = (A*)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A)
(5)是否调用构造函数/析构函数
ew/delete会调用对象的构造函数/析构函数从完成对象的构造/析构。malloc则不会调用。
(6)对数组的处理
c++提供了new[]与delete[]来专门处理数组类型。使用new[]分配的内存必须使用delete[]进行释放,两个要配套使用,不然会出现数组对象部分释放的现象,造成内存泄漏。
malloc动态分配一个数组的内存,需要手动自定数组的大小。
int ptr = (int )malloc(sizeof(int)*10);
//分配一个10个int元素的数组
(7)new与malloc是否可以相互调用
operator new/operator delete的实现可以基于malloc。而malloc的实现不可以去调用new。
(8)是否可以被重载
operator new/operator delete可以被重载。malloc/free并不允许重载
(9)能够直观地重新分配内存
使用malloc分配内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。new没有这样直观的配套设施来扩充内存。
(10)客户处理内存分配不足
在operator ew抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new_handler。对于malloc,客户并不能够去编程决定内存不足以分配时要干什么,只能看着malloc返回NULL。
9、 c++中头文件(.h)和源文件(.cpp)的区别?
头文件(.h)
写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现。
源文件(.cpp)
主要写实现头文件中已经声明的那些函数的具体代码。需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。那么当你需要用到自己写的头文件中的类时,只需要#include进来就行了。
10、 头文件中的#ifndef、 #define、 #endif的作用?
(1)作用:防止该头文件被重复引用。
“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:a.h文件#include“c.h”,而此时b.cpp文件导入了#include “a.h”和#include“c.h”,此时就会造成c.h重复使用。
(2)头文件被重复引用引起的后果。
有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言编译效率低下那将是一件多么痛苦的事情。
有些头文件重复包含,会引起错误。比如在头文件中定义全局变量(虽然这种方式不被推荐,但确实是c规范允许的)这种会引起重复定义。
(3)是不是所有的头文件中都要加入#ifndef、#define、#endif这些代码?
不一定要加,但是不管怎样,用ifndef…或者其他方式避免头文件重复包含,只有好处没有坏处。
例子:
#ifndef GRAPHICS_H //防止 graphics.h被重复引用
#define GRAPHICS_H
#include<math.h> //引用标准库的头文件
#include“header.h” //引用非标准库的头文件
...
void Function(...); //全局函数声明
...
class Box{ //类结构声明
...
};
#endif
11、 c++编译过程中的各个阶段都干了啥?
编译一般分为四个步骤:
预处理->编译->汇编->链接
gcc认为预处理的文件是c文件,并且设定c形式的连接
g++认为预处理的文件是c++文件,并且设定c++形式的连接
(1)编译预处理
预处理过程主要处理那些源文件中的以”#”开始的预编译指令,主要处理规则有:
将所有的”#define”删除,并展开所用的宏定义
处理所有条件预编译指令,比如“#if”、“#ifndef”、“#elif”、“#endif”。
处理”#include”预编译指令,将所包含的文件插入到该编译指令的位置,注:此过程是递归进行的。
删除所有注释
添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号。
保留所有的#pragma编译器指令。
(2)编译
编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分。
词法分析->语法分析->语义分析->中间代码生成->代码优化->目标代码生成->符号表管理->…
(3)汇编
汇编器是将汇编代码转化为机器可以执行的指令,每一条汇编语句几乎都是一条机器执行。经过编译、链接、汇编输出的文件称为目标文件。
(4)链接
链接的主要内容就是把各个模块之间相互引用的部分处理好,使各个模块可以正确的拼接。链接的主要过程包括地址和空间的分配、符号决议和重定位等步骤。
静态链接和动态链接的区别:
静态库里的代码在编译期就会嵌入可执行文件。
动态库在程序运行时,动态加载到内存,或者按照需要,随时被加载到内存。
动态库链接时,会在运行时选择需要的部分进行编译,生成的可执行文件会比较小,而且容易后续的更新。
静态编译会把所有的函数等都嵌入到可执行文件中,可以直接在任何电脑上直接运行,同样文件会远远大于动态链接的。
12、 c++中声明和定义的区别?
声明:是用以告诉编译器类型及其细节
例如:
class MyClass{
//数据成员细节
//成员函数细节
}
上述声明仅告诉编译器有自定义类型MyClass,编译器仅对其进行语汇分析及名字的决议,并未占用内存。
定义:即内存占有。编译器将在相对内存地址上为其对象定址。
注意:我们不能简单的说string mystring是声明还是定义,判断的原则是看是否占用内存。
总结:
(1)变量和对象不加extern永远是定义,类中的除外
(2)函数只有函数头是声明,有函数体的是定义
(3)类永远只是声明,类成员函数的函数体是定义
例子:
classMyclass{
staticintx;//这里的x是声明
staticconstinta;//这里的a是声明
//非static变量在类实例化时才分配内存
Myclass();//这里的函数是声明
}
intMyclass::x;//这是定义
constintMyclass::a=11;//这是定义
13、 c++的map和set的区别?
map、set属于标准关联容器,使用了非常高效的平衡检索二叉树:红黑树。它的插入删除效率比其他序列容器高是因为不需要做内存拷贝和内存移动,而直接替换指向结点的指针即可。
set是一种关联式容器,其特性如下:
(1)set以红黑树(RBTree)作为底层容器
(2)所有元素只有key没有value,value就是key
(3)不允许出现键值重复
(4)所有的元素都会被自动排序
(5)不能通过迭代器来改变set的值,因为set的值就是键。(STL中将set的迭代器设置为const,不允许修改迭代器的值)。对于set只能insert和delete
map和set一样是关联式容器,它们的底层容器都是红黑树,区别就在于map的值不作为键,键和值是分开的,它的特性如下:
(1)map以红黑树(RBTree)作为底层容器
(2)所有元素都是键+值存在
(3)不允许键重复
(4)所有元素是通过键进行自动排序的
(5)map的键是不能修改的,但是其键对应的值是可以修改的。
14、 vector、 list、 deque的区别?
(1)vector底层数据结构为数组,它拥有一段连续的内存空间,并且起始地址不变。支持快速随机访问,可以使用[]访问任意元素。vector每次扩容为原来的两倍,对小对象来说执行效率高,但如果遇到大对象,执行效率就低了。可以快速地在最后添加删除元素。
(2)list底层数据结构为双向链表,因此它的内存空间可以是不连续的,因此只能通过指针来进行数据的访问。可以快速地在所有地方添加删除元素,但是只能快速地访问最开始与最后的元素。
(3)deque:底层数据结构为一个中央控制器和多个缓冲区。支持首尾(中间不能)快速增删,也支持随机访问。deque是一个双端队列,也是在堆中保存内容的。它的保存形式如下:
[堆1]->[堆2]->[堆3]->…
每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品。
deque支持[]操作符,也就是支持随机存取,而且跟vector的效率相差无几,它支持两端的操作:push_back,push_front,pop_back,pop_front等。并且在两端操作上与list的效率也差不多。因此可以认为:deque是vector和list的折中。
总结:
(1)如果你需要高效的随机存取,而不在乎插入和删除的效率,使用vector
(2)如果你需要大量的插入和删除,而不关心随机存取,则应使用list
(3)如果你需要随机存取,而且关系两端数据的插入和删除,则应使用deque
vector::iterator和list::iterator的区别?
由于vector拥有一段连续的内存空间,能非常好的支持随机存取,因此vector::iterator支持“+”“+=”“<”等操作符。
而list的内存空间可以是不连续的,它不支持随机访问,因此list::iterator不支持“+”“+=”“<”等操作符运算,只能用“++”进行迭代。
15、 重载、重写、重定义的区别?
(1)重载(overload)
同一作用域中函数名相同,参数列表不同的函数,叫做重载。
基本条件:1、同一作用域。2、函数名相同。3、函数参数必须不相同(具体的参数列表不同表现在三个方面:参数类型不一样,参数个数不一样,参数顺序不一样,只有满足上述三个条件的一条或一条以上,才算做参数列表不同)。
注意:函数返回值可以相同,也可以不相同。
(2)重写(override)
重写也称为覆盖,子类重新定义父类中有相同名称和参数的虚函数,主要在继承关系中出现。
基本条件:
1、派生类中重写的函数和基类中被重写的函数必须为virtual函数
2、重写的函数和被重写的函数函数名和函数参数必须一致
3、重写的函数和被重写的函数返回值相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的类型是基类中被替换的虚函数所返回的指针或引用的类型的子类型。
注意:
(1)重写的函数所抛出的异常必须和被重写的函数所抛出的异常一致,或者是其子类。
(2)重写的函数的访问修饰符可以不同于被重写的函数,如基类的virtual函数的修饰符为private,派生类改为public或protected也是可以的。
(3)静态方法不能被重写,也就是static和virtual不能同时使用。
(4)重写的函数可以带virtual关键字,也可以不带。
(3)重定义(redefining)
也叫隐藏,子类重新定义父类中的非虚函数,屏蔽了父类的同名函数。
基本条件:被隐藏的函数之间作用域不相同。
注意:
1、子类和父类的函数名称相同,但参数不同,此时不管父类函数是不是virtual函数,都将被隐藏。(与重载的区别:重载要求在同一作用域内,而重定义是指父类和子类中)
2、子类和父类的函数名称相同,参数也相同,但是父类函数不是virtual函数,父类的函数将被隐藏。
16、 c++中const和define的区别?
两者都可以定义常量,区别在于:
(1)define是简单的文本替换,是在编译预处理进行,const是在编译时进行,const是分配内存的。const占用一份内存,define不占用内存但是会多处进行字符串替换。
(2)define不做类型检查,const可以进行类型的检查
(3)define可以定义一些简单的函数,const不可以
(4)define定义的常量不能调试,const可以进行调试
(5)const避免不要的内存分配,而且效率更高。define定义的常量在替换后运行过程中会不断地占用内存,在内存中有若干份copy,而const定义的常量存储在数据段,只有一份copy,效率更高。
17、 面向过程和面向对象的区别?
面向过程就是自顶向下逐步编程,分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。其最重要的是模块化的思想方法。
面向对象的方法主要是把事务给对象化,包括其属性和行为,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。面向对象就是你把要做的事情抽象成对象,告诉对象去做。面向对象三大特性(封装、继承、多态)使得在做复杂的事情的时候效率和正确率得到保证。
三大特性:
封装:把客观事物封装成抽象的类
继承:让某个类型的对象获得另一个类型的对象的属性的方法
多态:一个类实例的相同方法在不同情形有不同表现形式
18、 c++中深拷贝与浅拷贝的区别
(1)浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象,在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B,这时,如果B中有一个成员变量指针已经申请了内存,那A种的那个成员变量也指向同一块内存,这就出现了问题。当B把内存释放了(如析构),这时A内的指针就是野指针了,出现运行错误。
(2)深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。
19、 c++多态性和虚函数
多态性可以简单地概括为“一个接口,多种方法”。程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。
c++多态性是通过虚函数来实现的,只有重写了虚函数的才能算作是体现了c++多态性。多态的目的是为了接口重用,不论传递过来的究竟是哪个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。
20、 struct和class的区别?
(1)c中的struct和c++的class区别?
c是一种过程化的语言,struct只是作为一种复杂数据类型定义,struct中只能定义成员变量,不能定义成员函数。(c++中的struct可以包含成员函数,可以实现多态和继承)。
(2)c++中的struct和class的区别?
访问权限上:class中默认的成员访问权限是private,而struct中则是public。
继承上:class继承默认是private继承,而struct继承默认是public继承。
其他:class这个关键字还用于定义模板参数,就像typename,但关键字struct不用于定义模板参数。
21、 c++中iterator前++与后++效率区别?
STL中的遍历可以是以下两种之一
for(iterator it=begin();it!=end();++it){return it->second;}
for(iterator it=begin();it!=end();it++){return it->second;}
每次返回的结果是否相同?
答:两种方式iterator遍历的次数是相同的,但在STL中效率不同,前++返回引用,后++返回一个临时对象,因为iterator是类模板,使用it++这种形式要返回一个无用的临时对象,而it++是函数重载,所以编译器无法对其进行优化,所以每遍历一个对象,你就创建并销毁了一个无用的临时对象。除了特殊需要和对内置类型外,基本都是使用++it来进行元素遍历的。
填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved