C++ string小结1

本文深入探讨C++标准库中的string类,对比C语言字符串,强调其安全性与便捷性。介绍string类如何简化文本处理任务,避免常见错误,并讨论其内部存储机制及动态内存管理策略。
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_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();   
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值