1. C++ string是C++标准库的一部分
标准C++除了包含所有的标准C库外(其中有为支持类型安全而进行的少许增加与更改),还加进了自己特有的库,这些库比起标准C中的库,功能更加强大。可以说,它们的影响力几乎就等同于由C到C++的转变。
将string看作容器对象,它用迭代器来指示字符序列的开始与结尾。
STL中replace()算法的工作对象只是单一的对象,它不会替换引用char型数组或string对象。由于string很像一个STL序列,很多其他算法对它也适用,这些算法可以解决string类的成员函数没能直接解决的问题。
2.C++ string"对比"C语言字符串
标准C++ Sting类,可以简化在文本处理中可能遇到的大部分“琐事”。很有可能在C语言中需要使用多行代码才能完成的字符串处理操作,用string类中的一个成语函数调用就可以完成。
C++ string隐藏了它所包含的字符序列的物理表示。程序设计人员不必关心数组的维数或空结束符方面的问题。
C++字符串极大地减少了C语言编程中3种最常见且最具有破坏性的错误:超越数组边界,通过未初始化或被赋以错误值得指针来访问数组元素,以及在释放了某一数组原先所分配的存储单元后仍旧保留了“悬挂”指针。
C语言字符数组包括一个空字符。若由于疏忽或是其他差错,这个空结束符被忽略或重写,这个小小的差错就会使C语言的char型数组处理函数几乎不可避免地操作其已分配空间之外的内存,有时会带来灾难性的后果。
insert()函数使程序员放心地向字符串中插入字符,而不必担心会使存储空间越界,或者会改写插入点之后紧跟的字符。存储空间增大了,原有的字符会很“礼貌地”改变其存储位置,以便安置新元素。
数组下表表示[]与at()之间有一个重要的不同点。如果程序员想引用一个超过边界的数组元素,at()将会友好地抛出一个异常,而普通的[]下标语法将让程序员自行决策。
为了适应字符串中数据长度增长变化的需要,C++字符串动态且透明地扩充其内部的数据存储空间。当字符串中存储的数据增长超过最初分配给它的内存空间边界时,字符串对象就会进行存储管理调用,从堆中提取和归还存储空间。稳定的存储分配方案避免了内存泄露,并且有可能比“依靠自己转来转去”的内存管理方式更加有效。
3.C++ string存储分配机制
字符串分配规则明确规定:允许但不要求引用计数实现(reference-counted implementation),但无论其实现是否采用应用计数(reference counting),其语义都必须一致。
写时复制(copy-on-write)策略:只有当字符串被修改的时候才创建各自的拷贝,这种实现方式称为写时复制。当字符串只是作为值参数(value parameter)或在其他只读情形下使用,这种方法能够节省时间和空间。
在多线程程序中,几乎不可能安全地使用引用计数来实现。
当系统进行存储空间再分配遇到偶数字(word)的边界时,会隐含增加一个字节。为什么会这样呢?string类的设计者做过不懈的努力让char型数组和C++字符串对象可以混合使用,为此,在这种特定的实现中,预留一个字节以便很容易地容纳结束符。
4.其它
字符串的比较与数字的比较有其故有的不同。数字有恒定的永远有意义的值。为了评定两个字符串的大小关系,必须进行字典比较(lexical comparison)。字典比较的意思是,当测试一个字符看它是“大于”还是“小于”另一个字符时,实际比较的是它们的数值表示,而这些数值表示是由当前所使用的字符集的校对序列来决定的。
标准C++除了包含所有的标准C库外(其中有为支持类型安全而进行的少许增加与更改),还加进了自己特有的库,这些库比起标准C中的库,功能更加强大。可以说,它们的影响力几乎就等同于由C到C++的转变。
将string看作容器对象,它用迭代器来指示字符序列的开始与结尾。
STL中replace()算法的工作对象只是单一的对象,它不会替换引用char型数组或string对象。由于string很像一个STL序列,很多其他算法对它也适用,这些算法可以解决string类的成员函数没能直接解决的问题。
2.C++ string"对比"C语言字符串
标准C++ Sting类,可以简化在文本处理中可能遇到的大部分“琐事”。很有可能在C语言中需要使用多行代码才能完成的字符串处理操作,用string类中的一个成语函数调用就可以完成。
C++ string隐藏了它所包含的字符序列的物理表示。程序设计人员不必关心数组的维数或空结束符方面的问题。
C++字符串极大地减少了C语言编程中3种最常见且最具有破坏性的错误:超越数组边界,通过未初始化或被赋以错误值得指针来访问数组元素,以及在释放了某一数组原先所分配的存储单元后仍旧保留了“悬挂”指针。
C语言字符数组包括一个空字符。若由于疏忽或是其他差错,这个空结束符被忽略或重写,这个小小的差错就会使C语言的char型数组处理函数几乎不可避免地操作其已分配空间之外的内存,有时会带来灾难性的后果。
insert()函数使程序员放心地向字符串中插入字符,而不必担心会使存储空间越界,或者会改写插入点之后紧跟的字符。存储空间增大了,原有的字符会很“礼貌地”改变其存储位置,以便安置新元素。
数组下表表示[]与at()之间有一个重要的不同点。如果程序员想引用一个超过边界的数组元素,at()将会友好地抛出一个异常,而普通的[]下标语法将让程序员自行决策。
为了适应字符串中数据长度增长变化的需要,C++字符串动态且透明地扩充其内部的数据存储空间。当字符串中存储的数据增长超过最初分配给它的内存空间边界时,字符串对象就会进行存储管理调用,从堆中提取和归还存储空间。稳定的存储分配方案避免了内存泄露,并且有可能比“依靠自己转来转去”的内存管理方式更加有效。
3.C++ string存储分配机制
字符串分配规则明确规定:允许但不要求引用计数实现(reference-counted implementation),但无论其实现是否采用应用计数(reference counting),其语义都必须一致。
写时复制(copy-on-write)策略:只有当字符串被修改的时候才创建各自的拷贝,这种实现方式称为写时复制。当字符串只是作为值参数(value parameter)或在其他只读情形下使用,这种方法能够节省时间和空间。
在多线程程序中,几乎不可能安全地使用引用计数来实现。
当系统进行存储空间再分配遇到偶数字(word)的边界时,会隐含增加一个字节。为什么会这样呢?string类的设计者做过不懈的努力让char型数组和C++字符串对象可以混合使用,为此,在这种特定的实现中,预留一个字节以便很容易地容纳结束符。
4.其它
字符串的比较与数字的比较有其故有的不同。数字有恒定的永远有意义的值。为了评定两个字符串的大小关系,必须进行字典比较(lexical comparison)。字典比较的意思是,当测试一个字符看它是“大于”还是“小于”另一个字符时,实际比较的是它们的数值表示,而这些数值表示是由当前所使用的字符集的校对序列来决定的。
C_str()函数返回一个const char*, 它指向一个C语言风格的具有“空结束符”的字符串,此字符串与string对象的内容等价。当想将一个字符串传给一个标准C语言函数时,比如atoi()或<cstring>头文件中定义的任一函数,此时,const char*可派得上用场。不过,将c_str()的返回值作为非const参数应用于任一函数都是错误的。
下面代码中用"引用计数"的方法实现了部分的string 。
也可以参考“拷贝构造函数”;string的实现远比想象中复杂。
//代码参考自《More effective C++》Item M29
#include<string>
#include<iostream>
using namespace std;
class String
{
public:
String(const char *initValue = "");
String(const String& rhs);
String& operator=(const String& rhs);
const char& operator[](int index) const; // for const Strings (read)
char& operator[](int index); // for non-const Strings (read/write)
~String();
//获取引用计数次数,便于调试
void getRefCount()
{
cout<<"refCount = "<<value->refCount<<endl;
}
//隐式类型转换函数,比如:char* p = s1;
operator char*()
{
return value->data;
}
private:
//这种实现方法和我想象的不一样,我想象的是用static变量
//这里用内部new出来的value,每个新对象都指向这个堆对象
struct StringValue
{
int refCount;
bool shareable; //是否使用引用计数,引用计数初始为true, 当调用一次char& operator[](int index)后置为false
char *data;
StringValue(const char *initValue);
~StringValue();
};
StringValue *value; // value of this String
};
String::String(const char *initValue):value(new StringValue(initValue))
{
}
String::String(const String& rhs)
{
if (rhs.value->shareable)
{
value = rhs.value;
++value->refCount;
}
else
{
value = new StringValue(rhs.value->data);
}
}
String& String::operator=(const String& rhs)
{
if (value == rhs.value)
{
return *this;
}
if (--value->refCount == 0)
{
delete value;
}
value = rhs.value;
++value->refCount;
return *this;
}
const char& String::operator[](int index) const
{
return value->data[index];
}
char& String::operator[](int index)
{
if (value->refCount > 1)
{
--value->refCount;
value = new StringValue(value->data);
}
value->shareable = false;
return value->data[index];
}
String::~String()
{
if (--value->refCount == 0)
delete value;
}
String::StringValue::StringValue(const char *initValue): refCount(1),shareable(true)
{
data = new char[strlen(initValue) + 1];
strcpy(data, initValue);
}
String::StringValue::~StringValue()
{
delete [] data;
}
int main()
{
String s1 = "hello, world";
String s2 = s1;
String s3 = s2;
s2.getRefCount();
char* p = s1;
//使用指针改变string的值
p[0] = 'x';
s2.getRefCount();
cout<<s2<<endl;
String s4 = s2;
s2.getRefCount();
s2[0] = 'y';
s2.getRefCount();
String s5 = s2;
s2.getRefCount();
}