文件String.h

 1#ifndef _STRING_H
 2#define _STRING_H
 3
 4#include <ostream>
 5class String
 6{
 7public:
 8    String(const char* cszStr = "");
 9    String(const String& rhs);
10    String& operator=(const String& rhs);
11    ~String();
12
13    const char* c_str()const{return value->data;}
14
15public:
16    char& operator[](size_t idx);
17    const char& operator[](size_t idx)const;
18
19public:
20    struct StringValue
21    {
22        size_t refCount;
23        size_t len;
24        bool shareable;
25        char* data;
26    
27        StringValue(const char* cszStr);
28        ~StringValue();
29    }
;
30private:
31    StringValue* value;
32}
;
33#endif
34


文件String.cpp

 1#include "String.h"
 2#include <string.h>
 3#include <assert.h>
 4
 5String::String(const char* cszStr)
 6{             
 7    value = new StringValue(cszStr);
 8}

 9
10String::String(const String& rhs)
11{
12    if( rhs.value->shareable )
13    {                
14       value = rhs.value;
15       value->refCount++;
16    }

17    else
18    {
19        value = new StringValue(rhs.value->data);
20    }

21}

22
23String& String::operator=(const String& rhs)
24{
25    if( rhs.value == value )return *this;
26
27    if( rhs.value->shareable )
28    {
29        --value->refCount;
30        if( value->refCount == 0 )delete value;
31        
32        value = rhs.value;
33        ++value->refCount;
34     }

35     else
36     {
37         value = new StringValue(rhs.value->data);
38     }

39    return *this;
40}

41
42String::~String()
43{
44    if--value->refCount == 0 )delete value;
45}

46
47char& String::operator[](size_t idx)
48{
49    assert( idx < value->len && idx >= 0);
50    //if we are sharing value with other String Objects,
51    //break off a separate copy of value for ourselves
52    if( value->refCount > 1 )
53    {
54        value->refCount--;
55        value = new StringValue(value->data);
56    }

57    value->shareable = false;
58    return value->data[idx];
59}

60
61const char& String::operator[](size_t idx)const
62{
63    assert( idx < value->len && idx >= 0);
64    return value->data[idx];
65}

66
67String::StringValue::StringValue(const char* cszStr)
68    :refCount(1),shareable(true)
69{
70    len = strlen(cszStr);
71    data = new char[len + 1];
72    if( len == 0 )data[0= '\0';
73    else strcpy(data, cszStr);
74}

75
76String::StringValue::~StringValue()
77{
78    delete[] data;
79    data = 0;
80}

81

There are at least three ways of dealing with this problem. The first is to ignore it, to pretend it doesn't exist. This approach turns out to be distressingly common in class libraries that implement reference-counted strings. If you have access to a reference-counted string, try the above example and see if you're distressed, too. If you're not sure if you have access to a reference-counted string, try the example anyway. Through the wonder of encapsulation, you may be using such a type without knowing it.

Not all implementations ignore such problems. A slightly more sophisticated way of dealing with such difficulties is to define them out of existence. Implementations adopting this strategy typically put something in their documentation that says, more or less, "Don't do that. If you do, results are undefined." If you then do it anyway — wittingly or no — and complain about the results, they respond, "Well, we told you not to do that." Such implementations are often efficient, but they leave much to be desired in the usability department.

There is a third solution, and that's to eliminate the problem. It's not difficult to implement, but it can reduce the amount of value sharing between objects. Its essence is this: add a flag to each StringValue object indicating whether that object is shareable. Turn the flag on initially (the object is shareable), but turn it off whenever the non-const operator[] is invoked on the value represented by that object. Once the flag is set to false, it stays that way forever.

Here's a modified version of StringValue that includes a shareability flag.



至少有三种方法来应付这个问题。第一个是忽略它,假装它不存在。这是实现带引用计数的String类的类库中令人痛苦的常见问题。如果你有带引用计数的String类,试一下上面的例子,看你是否很痛苦。即使你不能确定你操作的是否是带引用计数的String类,也无论如何应该试一下这个例子。由于封装,你可能使用了一个这样的类型而不自知。

不是所以的实现都忽略这个问题。

稍微好些的方法是明确说明它的存在。通常是将它写入文档,或多或少地说明“别这么做。如果你这么做了,结果为未定义。”无论你以哪种方式这么做了(有意地或无意地),并抱怨其结果时,他们辩解道:“好了,我们告诉过你别这么做的。”这样的实现通常很方便,但它们在可用性方面留下了太多的期望。

第三个方法是排除这个问题。它不难实现,但它将降低一个值共享于对象间的次数。它的本质是这样的:在每个StringValue对象中增加一个标志以指出它是否为可共享的。在最初(对象可共享时)将标志打开,在非constoperator[]被调用时将它关闭。一旦标志被设为false,它将永远保持在这个状态(注10)。

版本2即是增加了共享标志的修改版本。

通过上述实现可以看出,这种实现方式还是有缺陷的,因为对于operator[],还是无法区分出是读操作还是写操作,如果在应用环境下,读操作很多,这样就会造成大量不必要的StringValue对象被复制,对内存和性能不利。

posted on 2010-07-13 14:37 mildcat 阅读(95) 评论(0)  编辑 收藏 引用 所属分类: C++

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理