移植Java代码到C++的技巧二.docx
- 文档编号:27090567
- 上传时间:2023-06-26
- 格式:DOCX
- 页数:12
- 大小:23.84KB
移植Java代码到C++的技巧二.docx
《移植Java代码到C++的技巧二.docx》由会员分享,可在线阅读,更多相关《移植Java代码到C++的技巧二.docx(12页珍藏版)》请在冰豆网上搜索。
移植Java代码到C++的技巧二
移植Java代码到C++的技巧二
二、字符串对象(stringvsString):
在Java的代码中,我们经常会看到这样一种写法,相信几乎每一个Java程序员都有过这样的代码,因此他看上去非常熟悉,甚至还带有一点儿亲切感。
1publicStringgetName(){
2returnname;
3}
这样的代码在Java中确实司空见惯,也无可厚非,因此对于我们来说没有太多可以讨论的空间,除非你非常希望了解JVM中对象常量池的概念,然而它并不是我们这个条目中将要讨论的主题。
那么现在让我们来看一下在C++中又是如何处理此类问题的,下面将列举出三种最常用的实现方式。
方法一:
直接返回内部name成员的指针。
1constchar*getName(){
2return_name;//_name变量的类型为char*
3}
方法二:
基于成员变量name的数据,重新分配相同长度的内存空间,之后再将name中的数据copy过来,最后返回函数中新分配的地址。
1char*getName(){
2size_tlength=strlen(_name);
3char*result=malloc(length+1);
4assert(result);
5memcpy(result,_name,length);
6result[length]='\0';
7returnresult;
8}
方法三:
基于成员变量name的数据,返回C++标准库中的string对象。
1stringgetName(){
2return_name;//这里_name成员变量的类型不是char*,而是string。
3}
首先需要肯定的是以上三种方法在一定程度上均能满足我们的需求,但也都存在各自的不足。
现在我们需要针对以上三种实现逐一给出我们的剖析。
1.直接返回内部数据的指针,这本身就是一个疯狂而又极度危险的实现方式。
因为对于函数调用者而言,可以随时通过返回的指针修改其所指向的数据,从而破坏了该函数所在对象的数据封装性。
事实上,我们在自行编写C++代码时,也是很少这样设计和实现此类函数的。
2.和第一种方法相比,在同样实现功能的基础上,确实避免了内部数据会被调用者修改的风险,然而这样的做法却带来了效率开销,而且还从另外一个方面破坏了该函数所在类的封装性。
先说效率问题,很明显该方法和方法一相比多了一次内存分配和内存拷贝的操作,而此操作对性能的影响程度也需要视情况而定。
至于封装性被破坏的问题其实也是非常明显的,因为返回值中的数据指针是在该函数内部被临时分配的,是需要被调用者自行释放的,因此对于调用者来说就需要关注该函数的内存分配方式,如果是malloc,调用者就需要使用对应的free函数来释放该内存空间,如果是new,则需要用delete[]的方式来释放。
一旦分配和释放内存的方式不匹配,将会导致极为严重而又难以察觉的堆内存混乱问题。
直接引发的后果就是程序在运行时极不稳定,随时都有崩溃的可能,而开发人员在定位此类问题时也是非常非常的困难,因为通常他们报错的方式比较随机。
有经验的开发者可以通过内存检测工具来帮助他们实现问题定位,然而在他们决定使用工具之前,则需要通过大量的其他手段来分析和判断该问题可能是内存混乱所致。
可以想象,这样一个微小的失误,给程序后期的调试和维护所带来的压力是难以估量的。
这里还需要额外指出的是,如果该函数(getName)和调用函数分属不同的动态库,那么对于调用者而言,即便内存释放的方式和分配时保持一致,仍然有可能导致内存混乱的问题发生。
至于具体原因,我们会在后面的条目中给出更为清晰的示例和解释。
3.和第一种方法相比,该方式也存在着同样的效率问题。
由于该函数返回的是string对象本身,而不是对象的指针或引用,因此在函数返回时,将产生一次拷贝构造,用来生成并初始化返回的string对象。
在该拷贝构造中,内存重新分配和内存数据拷贝等指令均将会被执行。
然而需要指出的是,和前两种方法相比,该方法避免了对象封装性被破坏和可能造成的堆内存混乱等问题的发生,从而更好的保证了程序在运行时的稳定性。
需要另行指出的是,在实际开发中的绝大多数情况下,调用者针对此类函数返回的字符串都仅仅是执行读操作,如数据比较、数据打印或直接插入数据库等。
只有在极个别的时候才会去主动修改它,因此额外的内存分配和拷贝操作所带来的开销往往是另人沮丧的。
在经过对上述三种方法的深入分析之后我们得出结论,为了更好和更彻底的解决字符串对象相关代码的移植问题,我们将不得不另辟蹊径,以找出运行效率较高、代码移植成本最低的方式。
下面将给出我在实际移植Java代码到C++代码的过程中所采用的方法,见如下代码片段:
1StringgetName(){
2return_name;//这里_name成员变量的类型不是char*,而是String。
3}
怎么会和Java的代码一样呢?
是的,你没有看错,就是和Java的代码一样,只是这里的String被我偷梁换柱了。
由于在移植过程中会经常遇到类似这样的代码,如果采用上述方法之一,其结果就是后面的代码移植工作将会变得异常艰难。
因为这些方法有的折损了C++语言本身在性能上的优势,有的则使本来就不够安全的C++代码(相比于Java)变得更加不安全,简直就是雪上加霜。
然而这仅仅是开始,也是整个移植过程中针对字符串对象所遇到的问题中的一小部分,更多的麻烦则源于对字符串对象的操作和使用上。
众所周知,Java中的String类型提供了大量的公有方法用于操作字符串对象,其功能无论是在丰富程度还是易用性方面均远远好于C++标准模板库中提供的string类型。
在性能方面,由于JVM为String等类型的对象提供了常量池机制,因此在以上Java代码中,将不会产生任何额外的内存数据拷贝指令。
为了解决上述诸多问题,我决定参照Java中String类型提供的常用功能,为我的C++程序重新实现一个与之对应String类型,这样不仅在性能上可以得到有效的保证,更重要的是在整个移植过程中,所有和字符串操作相关的代码移植将变得更加容易。
我认为这应该是一个一劳永逸的选择了,因为今后不管是移植其他Java代码,还是直接用C++实现新的功能,该String类型都将会有更多的用武之地。
见如下代码声明:
1classString
2{
3public:
4String();
5String(constString&other);
6String(constchar*otherText);
7String(constcharotherChar);
8String(constchar*otherText,constintcount);
9String(constchar*otherText,constintoffset,constintcount);
10explicitString(constintvalue);
11explicitString(constint64value);
12explicitString(constdoublevalue);
13~String();
14
15public:
16constString&operator=(constString&other);
17constString&operator=(constchar*otherText);
18constString&operator=(constcharotherChar);
19constString&operator+=(constString&other);
20constString&operator+=(constchar*otherText);
21constString&operator+=(constcharotherChar);
22constStringoperator+(constString&other)const;
23constStringoperator+(constchar*otherText)const;
24constStringoperator+(constcharotherChar)const;
25String&operator<<(constString&other);
26String&operator<<(constchar*otherText);
27String&operator<<(constcharotherChar);
28String&operator<<(constintotherDecimal);
29String&operator<<(constint64otherDecimal);
30String&operator<<(constfloatotherDecimal);
31String&operator<<(constdoubleotherDecimal);
32booloperator==(constString&other)const;
33booloperator==(constchar*otherText)const;
34booloperator==(constcharotherChar)const;
35booloperator!
=(constString&other)const;
36booloperator!
=(constchar*otherText)const;
37booloperator!
=(constcharotherChar)const;
38booloperator>(constString&other)const;
39booloperator>(constchar*otherText)const;
40booloperator>(constcharotherChar)const;
41booloperator>=(constString&other)const;
42booloperator>=(constchar*otherText)const;
43booloperator>=(constcharotherChar)const;
44booloperator<(constString&other)const;
45booloperator<(constchar*otherText)const;
46booloperator<(constcharotherChar)const;
47booloperator<=(constString&other)const;
48booloperator<=(constchar*otherText)const;
49booloperator<=(constcharotherChar)const;
50constcharoperator[](constintindex)const;
51operatorconstchar*()const;
52
53public:
54voidappend(constchar*otherText,intcount=-1);
55boolisEmpty()const;
56size_tlength()const;
57size_tcapacity()const;
58boolequals(constchar*otherText,constsize_tcount,boolignoreCase)const;
59boolequalsIgnoreCase(constString&other)const;
60boolequalsIgnoreCase(constchar*otherText)const;
61boolequalsIgnoreCase(constcharotherChar)const;
62intcompareIgnoreCase(constString&other)const;
63intcompareIgnoreCase(constchar*otherText)const;
64intcompareIgnoreCase(constcharotherChar)const;
65boolstartsWith(constchar*otherText,boolignoreCase)const;
66boolstartsWith(constcharotherChar,boolignoreCase)const;
67boolendsWith(constchar*otherText,boolignoreCase)const;
68boolendsWith(constcharotherChar,boolignoreCase)const;
69StringtoUpperCase()const;
70StringtoLowerCase()const;
71constStringsubstring(constintstartPos,constintcount)const;
72constStringsubstring(constintstartPos)const;
73boolcontains(constchar*childText,boolignoreCase)const;
74boolcontains(constcharchildChar,boolignoreCase)const;
75boolcontainsAnyOf(constchar*childText)const;
76intindexOf(constchar*childText,boolignoreCase)const;
77intindexOf(constcharchildChar,boolignoreCase)const;
78intindexOf(constintstartPos,constchar*childText,boolignoreCase)const;
79intindexOf(constintstartPos,constcharchildChar,boolignoreCase)const;
80intindexAnyOf(constchar*childText,boolignoreCase)const;
81intindexAnyOf(constintstartPos,constchar*childText,boolignoreCase)const;
82intlastIndexOf(constchar*childText,boolignoreCase)const;
83intlastIndexOf(constcharchildChar,boolignoreCase)const;
84intlastIndexAnyOf(constchar*childText,boolignoreCase)const;
85Stringtrim()const;
86Stringltrim()const;
87Stringrtrim()const;
88Stringinsert(constintpos,constcharnewChar)const;
89Stringinsert(constintpos,constchar*newText)const;
90Stringreplace(intstartPos,intcount,constchar*newText,constinttextCount);
91inttoIntValue(bool*ok=0)const;
92int64toInt64Value(bool*ok=0)const;
93doubletoDoubleValue(bool*ok=0)const;
94booltoBoolValue()const;
95constchar*text()const;
96
97private:
98constchar*_buffer;
99size_t_dataLength;
100}
从以上声明中可以看出,我们为String类型提供了大量且实用的接口方法,其中有相当一部分借鉴于Java中的String类型。
相比于C++标准模板库中提供的string类型,新实现的String类型有以下几点优势:
1.提供了更完整的构造函数重载方法,以使该类型的对象与其他类型,特别是和原始数据类型之间的交互更为便利。
2.充分利用了C++中提供的操作符重载机制,使该类型的对象在使用上和原始数据类型更为贴近。
3.Java中String类型的常用方法在该类中均能找到与之对应的方法。
仅从上述第三点看,在整个代码移植过程中,由于几乎所有和字符串操作相关的功能在我们新实现的String类型中均能找到匹配的方法,信不信由你,这一点至关重要,因为由此而提升的代码复用程度可以大大缩短我们的移植周期,同时也降低了代码出错的几率。
现在让我们重新回到getName()函数的效率问题上,就目前而言,新的String类型在效率方面和STL中的string类型几乎是一样的,都同样会有对象拷贝动作的发生,同时也同样避免了getName()函数所在类的封装性被破坏的问题。
简而言之,目前的实现方式和方法三相比存在着同样的优势和劣势,所以我们现在需要做的就是如何消除额外的内存重新分配和内存数据拷贝等操作。
为了解决这一棘手问题,我们将不得不在新的String类型中应用C++中比较常用的对象资源管理机制----引用计数,下面我们还是先看一下修订后的String类型的代码声明,之后再给出详细解释。
1classString
2{
3public:
4String();
5String(constString&other);
6String(constchar*otherText);
7......//省略的构造函数重载方法和上面的声明相同
8~String();
9
10public:
11constString&operator=(constString&other);
12constString&operator=(constchar*otherText);
13constString&operator=(constcharotherChar);
14......//省略的操作符重载方法和上面的声明相同
15
16
17public:
18voidappend(constchar*otherText,intcount=-1);
19boolisEmpty()const;
20size_tlength()const;
21......//省略的共有方法和上面的声明相同
22
23
24private:
25classInnerRefCountedString:
publicRefCountedBase
26{
27public:
28InnerRefCountedString(constsize_tcount)
29//(count+2)是为了保证count为1的时候,不会致使_containSize为0
30:
_containSize(calculateContainSize(count)),_count(count){
31_text=newchar[_containSize+1];
32assert(_text);
33}
34
35virtual~InnerRefCountedString(){
36delete[]_text;
37}
38
39voidcopyData(constchar*otherText,constsize_tcount){
40assert(0!
=otherText&&count<=_containSize);
41memcpy(_text,otherText,count);
42_count=count;
43_text[_count]=0;
44}
45
46voidappendData(constchar*appendText,constsize_tappendCount){
47assert(0!
=appendText&&appendCount+_count<=_containSize);
48memcpy(_text+_count,appendText,appendCount);
49_count+=appendCount;
50_text[_count]=0;
51}
52
53char*getText()const{
54return_text;
55}
56
57staticsize_tcalculateContainSize(constsize_ts){
58return((s+2)>>1)*3;
59}
60......//省略了该类的部分接口方法。
61private:
62size_t_containSize;
63size_t_count;
64char*_text;
65};
66
67private:
68typedefRefCountedPtr
69SmartString
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 移植 Java 代码 C+ 技巧