所在的位置: c++ >> 编程思想 >> $c++$C++的反思

$c++$C++的反思

C++的反思

来自:SkywindInsid

作者:skywind

链接:

最近两年C++又有很多人出来追捧,并且追捧者充满了各种优越感,仿佛不写C++你就一生是低端程序员了,面对这类现象,要不要出来适时的黑一下C++呢?呵呵呵。

我们要有点文娱精神,关于C++的笑话数都数不清:

笑话:C++是一门不吉祥的语言,听说波音公司之前用ADA为飞机硬件编程,一直用的好好的,后来招聘了一伙大学生,学生们说我靠还在用这么落后的语言,然后换成C++重构后飞机就坠毁了。

笑话:什么是C++程序员呢?就是本来10行写得完的程序,他非要用30行来完成,并自称“封装”,但每每到第二个项目的时候却将80%打破重写,并美其名曰“重构”。

笑话:C容易擦枪走火打到自己的脚,用C++虽然不容易,但一旦走火,就会把你整条腿给炸飞了。

笑话:同时学习两年Java的程序员在一起讨论的是面向对象和设计模式,而同时学习两年C++的程序员,在一起讨论的是tmplat和各种语言规范到底怎么回事情。

笑话:教他人学C++的人都挣大钱了,而很多真正用C++的人,都死的很惨。

笑话:C++有太多地方可以让一个人表现自己“很聪明”,所以使用C++越久的人,约觉得自己“很聪明”结果步入圈套都不知道,掉坑里了还觉得估计是自己没学好C++。

笑话:好多写了10多年C++程序的人,至今说不清楚C++到底有多少规范,至今依然时不时的落入某些坑中。

笑话:很多认为C++方便跨平台的人,实际编写跨平台代码时,都会发现自己难找到两个支持相同标准的C++编译器。

—————

Q:那C++为何还能看到那么多粉丝呢?

A:其实是由于Windows,由于Windows的兴起带动了C++,C++本来就是一门只合适开发GUI的语言。

Q:为什么C++只合适开发GUI呢?

A:你看Unix下没有GUI,为啥清一色的C呀?所有的系统级问题都能在C里找到成熟的解决方案,运用级问题都能用其他高级语言很好地解决,哪里有C++甚么事情呀?

Q:你强词夺理,Unix下也有C++的项目呀。

A:有,没错,你任然可以用任何语言编写任何糟的代码。

Q:别瞎扯了,你都在说些甚么?连C++和Windows都扯到一起去了。

A:回想下当年的情形,一个大牛在教一群初学者如何编程。一边开发一边指着屏幕上说,你看,这是一个Button,我们可以用一个对象来描写它,那是一个panl我们也可以用一个对象来描写它,并且你们有没有发现,其实Button和Panl是有血缘关系的,你们看。。。这样就出来了。。。。下面的学生之前都是学着学校落后的教材,有些乃至还在用turboc的bgi库来画一些点和圆。哪里见过这么这么华丽的Windows界面呀。大牛说的话,象金科玉律一样的铭刻在自己幼小的心理。一边学着Windows,一边发现,果然,他们都需要一个基类,果然,他们是兄弟关系,共同包括一些基本属性,可以放到基类去。他们越用越爽,潜意识里觉得由于C++这么顺利的帮他们解决那么多界面问题,那看来C++可以帮他们解决一切问题了。因而开发完界面以后,他们继续开发,当他们碰到各种设计问题时,反而认为肯定自己没有用好C++。因而强制自己用下去,然后就完蛋了。

(点击mor展开)

—————

关于C++的笑话我有1箩筐,各位C++粉用不着对号入座。言归正传,为何要黑C++呢?谈不上黑不黑,我从94年开始使用C++(先前是C和Pascal),一路看着C++成长壮大,用C++写过的代码,加起来应当超过10MB了吧,C++的各种宝典我也都读过,1直到年开始切回C,主要原因是发现很多没法用C++思路继续解决下去的问题,或说用C++思路解决下去会很糟的问题。

那时候(-)正是C++满天飞的时候,言必称C++,用必用模版,我跳出来讲你们醒醒吧,别过火了,这个世界并不是都是抽象数据结构和算法就可以描写清楚的。因而很多人激动的跳出来讲:“你没领会到C++精华,你根本都不会用C++”。我问他们:“语言是用来解决问题的,如果一个语言学了三四年都会常常掉沟里,算好语言么?如果编写10多年C++的程序员都很难掌握得了,这算好语言么”。他们又说:“语言是死的,人是活的”。

我记得当时一名国内C++大牛,为了纠正我的“毛病观点”,给我看过他写的一套十分强大的库,我打开一看,倒吸了一口冷气,全部是.h文件。我只能回他三个字:“你牛逼”。固然这是一个极端的例子,那家伙后来终究也开始把.h里面的东西逐渐挪到.cpp里面了,这是好事。

当时和云风在一家公司,年新人培训时,他给新人布置了一个实现内存分配器的作业,批改作业的时候,他常常边看边问人家,“不够C++呀,你能不能百分之百OOP?”,“1%的C都不要留”。我当时在公司内部邮件列表里面发过关于C++的问题,大部分人都表示:“你看没有C++我们怎样写3D引擎呢?”。我跟他们讲:“JohnCarmack直到Quak3都还在用着ANSIC,后来由于不得不支持D3D,改用C++了。为啥C不能写3D引擎了?”。他们告诉我:“你看,Point,就是个对象,Matrix也是个对象,那么多Vctor的代数计算,用C++的算术重载是多么美好的事情,三维世界就是对象的世界。”。

确切当时客户端GUI的话,只有C++,图形引擎也只有C++,这两个正是C++最强的地方,所以我也没和他们辩论,强制他们承认C也可以很漂亮的写图形,而且C写的可以写的很优雅。我又不是闲着没事情,何必去质疑人家的核心价值观呢,呵呵。当年我正在接手一个C++项目,代码超过KB,每次崩溃都需要花费很长时间去定位,项目中大量的前后依赖,改一个地方,前后要看好几处,1处遗漏,全部系统就傻逼了。我开始重构后,画了两个星期,将性能敏感的核心部份剥离出来用C实现(代码量仅KB),然后导出Python接口,用Python来完成剩下的部份,全部脚本层代码量只有KB。整个世界清新了,全部C++项目原来的工期为2个程序员四个月,我一个人重构的时间加起来就1.5个月,而且代码量比远来少了两倍还多,各种奇特的BUG也一扫而尽。我看看左侧的KB一团乱麻的C++代码,再看看右侧整洁的多KB纯C+Python,揣摩着,这个项目干吗不一开始就这么做?

跨语言接口

现代项目开发,不但需要更高的性能,而且需要更强大的语言描写能力。而C++正处在一个为难的地方,比底层,它不如C能够精确的控制内存和硬件,各种隐式构造让你防不胜防;比描写能力,比快速业务开发和毛病定位,它又赶不上Python,Ruby,Lua等动态语言,处于东线和西线同时遭受挤压和蚕食的地步。

很快,年左右,其他项目组各种滥用C++的问题开始显现出来:当时脚本化已在工程实践中取得极大的成功,但是某些项目一方面又要寻求%的C++,另一方面又需要对脚本导出接口,他们发现问题了,不知道该怎样把大量的C++基础库和接口导给Lua。

C的接口有各种方便的方式导给脚本,但是全部项目由一群历来就不消于使用脚本的cpp大牛开发出来,当他们要吧cpp类导出接口给脚本时,他们设计了一套牛逼的系统,lua自动生成机器码,去调用c++的各种类,没错,就是c++版本的cffi或ctyps。他为调用vc的类写了一套机器码生产,又为调用gcc的类写了一套代码生成。那位cpp大牛写完后四周夸耀他的成果,后来他离职了,项目上线一而再再而三的出现无可查证的问题,后来云风去支援那个项目组,这套盘根错节的c++项目,这套盘大的代码自生成系统深深的把他给恶心到了。后来尽人皆知云风开始反C++,提倡回归C了,不知道是不是和这个项目有关系。

因而发现个有趣的现象,但凡善于使用脚本来提高工程效力的人,基本都是C加动态语言解决大部分问题(除gui和图形),但凡认为c++统治宇宙的人很多都是历来没使用过脚本或用了还不知道该怎样去用的人。

凭仗这样的方法,我们的产品同竞争对手比拼时,一样一个功能,一样的人力配置,竞争对手用纯C++要开发三月,我们一个月就弄出来了,一样的时间,对手只能试错一次,我们可以试错三次。后来,据我们招聘过来的同事说,竞争对手也开始逐渐下降C++的比例,增加java的比例了,这是好事,大家都在进步嘛。

ABI的为难

ABI级别的C++接口从来没有标准化过,以类为接口会引入很多隐藏问题,比如内存问题,一个类在一个库里面实例化的,如果再另外一个库里面释放它们就有很多问题,由于两个动态库可能内存管理系统是不一样的。你用这里的allocator分配一块内存,又用那里的allocator去释放,不出问题才怪。很多解决方法是加一个Rlas方法(比如DX),告知外面的人,用完的时候不要去dlt,而是要调用Rlas。

项目写大了各个模块隔离成动态库是很正常的,而各种第三方库和自己写的库为寻求高性能引入特定的内存管理机制也是很正常的。很多人不注意该调用rlas的地方错写成dlt就掉沟里去了。更有胜者跨ABI定义了很多inlin方法的类,结果各种隐式构造和析构其实在这个库里生成,那个库里被析构,乱成一团乱麻。C就清晰很多,构造你就调用fopn,析构你就fclos,没有任何歧义。其实C++的矛盾在于一方面承认作为系统级语言内存管理应当交给用户决定,一方面自己却又定义很多不受用户控制的内存操作行动。所以跨ABI层的c++标准迟迟没法被定义出来,不是由于多态abi复杂,而是由于语言逻辑出现了相互矛盾。为了弥补这个矛盾,C++引入了opratornw,dlt,这nw/dlt重载是一个补钉并没从逻辑上让语言变得完备,它的出现,进一步将使用者拖入bug的深渊。

其实今天我们回过头去看这个问题,能发现两个基本原则:跨abi的级别上引入不可控的内存机制从语言上是有问题的,只能要靠开发者约定各种灵巧的基类和约定开发规范来解决,这个问题在语言层是解决不了的;其次你既然定义了各种隐式构造和析构,就该像java活着动态语言一样完全接收内存,不允许用户再自定义任何内存管理方法,而不是一方面作为系统极语言要给用户控制的自由,一方面自己又要抢着和用户一起控制。

因此对象层ABI接口迟迟没法标准化。而纯C的ABI不但可以轻松的跨动态库还能轻松的和汇编及各类语言融会,不是由于C设计多好,而是C作为系统层语言没有去管它不该管的东西。当年讨论到这个话题时C++大牛们又开始重复那几句金科玉律来反驳我:“语言只是招式,你把内功练好,就能做到无招胜有招,拿起草来都可以当剑使,C++虽然有很多坑,你把设计做好不那末用不就行了”。我说:本来应当在语言层解决好的事情,由于语言逻辑不完备,将大量问题抛给开发者去解决极大的增加了开发者的思惟负担,就像破屋上表浆糊一样。你金庸看多了吧,武术再高,当你拿到一把枪发现子弹不一定往前射,偶尔还会往后射时,请问你是该专心打敌人呢?还是时刻要防备自己的子弹射向自己?

系统层的挫败

C++遭受挫败是进军嵌入式和操作系统这样靠近硬件层的东西。大家觉得宇宙级别的编程语言,自然能够胜任一切任务,很快发现几个问题:

●没法分配内存:原来用C可以完全不依赖内存分配,代码写几千行一个malloc没有都行。嵌入式下处理器加电后,跳到特定地址(比如起始地址0),第一条指令一般用汇编来写,固定在0地址,就是简单初始化一下栈,然后跳转到C语言的start函数去,试想此时内存分配机制都还没有建立,你定义了两个类,怎样构造呀?资源有限的微处理器上大部分时候就是使用一块静态内存进行操作。C++写起来写爽了,各种隐式构造1出现,就傻了。

●标准库依赖:在语言层面,C语言的所有特性都可以不用依赖任何库就运行,这为编写系统层和跨平台跨语言代码带来了很方便的特性。而C++就不行,我要构造呀,我要异常呀,你为啥不能给我强大的运行时呢?甚么你还想用stl?不看看那套库有多臃肿呀(内存占用,代码尺寸)。

●异常处理问题:底层开发需要严格的处理所有毛病返回,这一行调用,下一行就判断毛病。而异常是一种疏松的毛病处理方式,运用层这么写没问题,系统层这么写就很狼狈了。每行调用都try一下和C的调用后if判断结果有甚么区分?C++的构造函数是没有返回值的,如果构造内部出错,就必须逼迫你catch构造函数的异常,即使你catch住了,构造异常的时候固然会自动触发相干内部对象的析构,但是有很多并没有析构的资源(比如系统资源,比如C接口的资源,他们都没有一个析构),整个过程是很难控制的,此时这个实例是一个半初始化实例,你该怎样处理它呢?因而有人把初始化代码移除构造函数,构造时只初始化一下变量,新增加一个带返回的init函数,这样的代码写的比C冗余很多。何况硬件中断产生时,在你不知道的情况下,同事调到一些第三方的库,你最外层没有把新的xcption给catch住,这个xcption该往哪里抛呀?内存不够的时候你想抛出一个OutOfMmoryExcption,可是内存已不够了,此时完全无能力构造这个异常又该怎么办呢?

●处理器兼容:C++的类依赖基地址+偏移地址的寻址方式,很多非Intl系列的微处理器上只有简单的给定地址寻址,不支持这样一条语句实现BASE+OFFSET的寻址,很多C++代码编译出来需要更多的指令来运算地址,致使性能下落很多,得不偿失。

●隐式操作问题:C的特点是简单直接,每行语句你都能清楚的知道会被翻译成甚么模样,系统会严格依照你的代码去履行。而用C++,比如str1=str2+Hllo+str3;这样的语句,没几个人真的说得清楚究竟有多少次构造和拷贝,这样的写法编写底层代码是很不负的,底层需要更加精细和严格的控制,用C语言控制力更强。

固然,说道这里很多人又说,“C++本来就是C的超集,特定的地方你完全可以依照C的写法来做呀。没人强制你构造类或使用异常呀”,没错,按Linus的说法:“想要用C++写出系统级的优秀的可移植和高效的代码,终究还是会限于使用C本身提供的功能,而这些功能C都已完善提供了,所以系统层使用C的意义就在于在语言层排除C++的其他特性的干扰”。

很多人都记得Linus在7年由于有人问Git为何不用C++开发炮轰过一次C++。事实上年C++如日中天的时候,有人问Linux内核为什么不用C++开发,他就炮轰过一次了:

实际上,我们在年就尝试过在Linux使用C++了。很恶心,相信我,用C++写内核是一个“BLOODYSTUPIDIDEA”。事实上,C++编译器不值得信任,年时它们更糟,而一些基本的事实从没改变过:

–整套C++异常处理系统是“fundamntallybrokn”。特别对编写内核而言。

–任何语言或编译器喜欢在你背后隐藏行动(如内存分配)对开发内核并不是一个好选择。

–任然可以用C来编写面向对象代码(比如文件系统),而不需要用C++写出一坨屎来。

总得来讲,对任何希望用C++来开发内核的人而言,他们都是在引入更多问题,没法象C一样清晰的看到自己到底在写甚么。

C++粉丝们在C++最火热的时候试图将C++引入系统层开发,但是从来没有成功过。所以不管是嵌入式,还是操作系统,在靠近硬件底层的开发中,都是清一色的C代码,完全没有C++的立足之地。

运用层的反思

STL出来后,给人一种C++可以方便开发运用层逻辑的错觉。由于很多语言层不周密的事情,让STL来以补钉的方式完成,因而很多以为可以象写java一样写C++的初学者落入了一个个的坑中。比如z(),在Windows下vc的stl是保存了list的长度的,siz()直接O(1)返回该变量,而在gcc的stl中,没有保存list长度,siz()将搜索所有节点,O(n)的速度返回。

由于语言层不支持字符串,致使std::string实现十分不统一,你拷贝构造一个字符串,有的实现是援用,才用copy-on-writ的方法援用。有的地方又是nw,有的实现又是用的内存池,有的实现线程安全,有的实现线程不安全,你完全没法说出同一个语句后面到底做了些甚么(见孟岩的《Linux之父话糙理不糙》)。

再比如说我想使用hash_map,为了跨平台(当你真正编写跨平台代码时,你很难决定目标编译器和他们的版本,想用也用不了unordrd_map),我很难指出一种唯一声明hash_map的方法,为了保证在不同的编译器下正常的使用hash_map,你不能不写成这样:

#ifdf__GNUC__

#ifdf__DEPRECATED

#undf__DEPRECATED

#ndif

#includxt/hash_map

namspacstdxt{usingnamspac__gnu_cxx;}

namspac__gnu_cxx{

tmplatstructhashstd::string{

siz_toprator()(conststd::stringx)const{

rturnhashconstchar*()(x.c_str());

}

};

}

#ls

#ifndf_MSC_VER

#includhash_map

#lif(_MSC_VER1)

#includmap

#dfinIHAVE_NOT_HASH_MAP

#ls

#includhash_map

#ndif

#ndif

#ifdf__GNUC__

usingnamspac__gnu_cxx;

typdfhash_mapuint32_t,XXXX*HashXXXX;

#ls

usingnamspacstdxt;

typdfhash_mapuint32_t,XXXX*HashXXXX;

#ndif

如果有更好的跨平台写法,麻烦告诉我一下,实在是看不下去了。一个基础容器都让人用的那末辛苦,使得很多C++程序员成天都在思考各种规范,没时间真正思考下程序设计。

由于语言层要兼容C,又不肯象C一样只做好系统层的工作,致使当C++涉足运用层时,没法接收内存管理,没法支持语言层字符串,没法实现语言层基础容器。所以需要借助一些stl之类的东西来提供便利,但stl本身又是充满各种坑的。且不说内存占用大,程序体积大等问题,当编译速度就够戗了。所以为何C++下面大家乐意重复造轮子,实现各种基本容器和字符串,致使几近每一个不同的C++项目,都有自己特定的字符串实现。就是由于大家踩了坑了,才开始觉得需要自己来控制这些细节。stl的出发点是好的,但是只能简单小程序里面随意用一下,真是大项目用,stl就容易把人带沟里了,所以很多大点的C++项目都是自己实现一套类似STL的东西,这难道不是违背了stl设计的初衷了么?

语言层的缺失,让大家为了满足业务开发的快速迭代的需求,创造了很多很基础的设计灵巧的基类,来提供类似垃圾回收,援用计数,copy-on-writ,dlgat,等数不胜数的功能。每一个项目都有一系列BasObjct之类的基础类,这样就引入一个误区,两年后你再来看你的代码,发现某个BasObjct不满足需求了,或你和另外一个项目mrg代码时,需要合并一些根本属性。图形和GUI这些万年不变的模型还好,运用类开发千变万化,一旦这些设计灵巧的基类不再适应项目发展时,常常面临着全面调剂的代价。

打开一个个C++大牛们blog,很多地方在教你std::string的原理,需要注意的事项。map的限制,vctor的原理,教你如何实现一个string。这就叫“心智负担”,分散你的注意力,这是其他语言里历来见不到的现象。战士不研究怎样上前线杀敌,每天在揣摩抢和炮的原理,成天在思考怎样用枪不会走火,用炮不会炸到自己,这战还怎样打?

所以尔后几年,愈来愈多的人开始反思前两年C++过热所带来的问题,比如高性能络库ZroMQ作者MartinSustrik的:《为何我希望用C而不是C++来实现ZroMQ》,比如云风的《云风的BLOG:C的回归》,比如引发热议的《WhyC++IsNotBack》。

全面被代替

8年以后,行业竞争愈来愈剧烈,正当大家一边苦恼如何提高开发效力,一边掉到C++的各种坑里的时候,愈来愈多的运用开发方案出现出来,他们都能很好的代替C++。各行各业的开发者逐渐相见恨晚的发现了各种更加优秀的方案:需要底层控制寻求性能的设计,大家退回到C;而需要快速迭代的东西大家找到各种动态语言;介于性能和开发速度之间的,有java,知乎上好像很多黑java的,语言是有不足,但是比起C++好很多,没那么多坑,真正斟酌面向对象,真正让人把心思放在设计上。所以再黑也不能挡住java在tiob上和C语言不是第一就是第二的事实,再黑也挡不住java在云计算,分布式领域的卓着贡献。

所以年以后,C++处在一个全面被代替的进程中:

●底层系统:进一步回归C语言,更强的控制力,更精确的操作。

●页开发:6年左右,C++和fastcgi就被一起赶出wb世界了。

●高性能服务:varnish,nginx,rdis等新的高性能络服务器都是纯C开发的。

●分布式运用:7年左右,C++被java和其他动态语言完全赶跑。

●游戏服务端:8年后进一步进化为C和脚本,完全看不到胖C++服务端了。

●并行计算:年后,go,scala,rlang;而能方便同go接口的,是C不是C++。

●游戏引擎:没错C++和脚本,但是这年头愈来愈多的开源引擎下,引擎类需求愈来愈少。

●游戏逻辑:脚本

●多媒体:SDL纯C,ffmpg是纯C,wbrtc的核心部份(DSP,codc)是纯C的。

●移动开发:早年C++还可以开发下塞班,现在基本被java+objc+swift赶跑了。

●桌面开发:Qt+Script,C#等都能做出漂亮的跨平台界面。且界面脚本化趋势,不需要C++了。

●页前端:JavaScript,Html5,Flash

●操作系统:FrBSD,OpnSolaris,Linux,RTOS,Darwin(OSX底层),都是纯C

●虚拟技术:qmu/kvm(云计算的基石)纯C,Xn纯C

●数据库:MySQL(核心纯C,外围工具C++),SQLit纯C,PostgrSQL/BDB/unqlit纯C

●编译器:C/C++并存,不过编译器用脚本写都没关系,我还在某平台用java写的C/C++编译器

●大数据:kafka,hadoop,storm,spark都使用Java/Jvm系列技术

●云存储:opnstackswiftpython,hdfsjava,还有好多方案用go

可以看出,即使C++的老本行,GUI和图形(确切也还存在一些短期内C++没法替换的领域,就像交易系统里还有COBOL一样),这年头也面临的愈来愈多的挑战,比如新发布的Rust(如何看待Rust的运用前景?–知乎用户的回答)。可以发现,开发技术多元化,用最合适的技术开发最合适的运用是未来的趋势。而为这些不同的技术编写高性能的可控的公共组件,并轻松的和其他语言接口,正是C语言的强项。所以不管运用层语言千变万化,对系统级开发语言C的需求还是那末的稳定,而这个进程中,哪里还有C++的影子呢?

话题总结

所以说未来的趋势是:Cx各种语言混搭的趋势,从TIOBE上C++的指数十年间下跌了三倍可以看出,未来还会出现出更多技术来代替各个角落残余的C++方案,C++的使用情况还会进一步下落。所以题主问学习纯C是不是有前途,我觉得如果题主能够左手熟练的掌握C语言,培养系统化的思惟习惯和精确控制内存和硬件的技能;右手继续学习各种新兴的开发技术,能够应对各个细分领域的快速开发,碰到新问题时能左右开弓,那末未来工作上肯定是能上一个大台阶的。至于C++嘛,有时间看看就行,必不得以要保护他人代码的情况下写两行便可。

故事分享

古代用弓箭进行远距离攻击时,对射手要求较高,瞄准难度大,需要一直用力保持准心。战役中一个弓箭手开弓二十次就需要比较长的休息时间。弩的威力远胜于弓,秦弩的制造就如现代的自动步枪一般精密无2,它既可以延长射击,又可以精确瞄准。弩箭的发射速度更是弓箭的数倍,威力惊人。由于弩的操作非常简单,不需要射击技能,平民很容易掌握它的使用方法。秦国靠着弩兵,在战争中取得了很多优势,被人称为“虎狼之师”。

日本投降时,天皇下罪己诏。很多兵士不愿意相信这时候真的,找种种理由谢绝相信。有的兵士乃至以为天皇的广播是敌人诱降的把戏,因而躲到丛林里继续三五成群的搜集情报,攻击可以攻击的目标,等待上司来给他们下达新命令。直到好几年后看到周围的人都穿着平常的便装了,而来巡山的“敌人”也从兵士变为了巡逻队,他们都还觉得这是敌人的假装。而同时,德国战败时,最后的党卫军一直战役到年才肯投降。

—————————————–

很多人觉得java慢,C++快java10倍以上已是上世纪的事情了,现代的java只比C/C++慢70%,C++连1倍都快不了java。也不要觉得动态语言慢,javascript只比C/C++慢2.7倍。luajit只比C++慢5.8倍。在jit技术发展的今天,C++在性能上离动态语言/java的差距愈来愈小,可易用性和生产效力上的差距,却和动态语言/java比起来愈来愈大。

—————————最后,补充一张图:

●本文编号38,以后想浏览这篇文章直接输入38便可。

●输入m可以获得到全部文章目录
































白癜风的治疗
白癜风医院



转载请注明:http://www.nydjfy.com/bcxx/634.html