一个简单的string类Str

看到《accelerated c++》的一个string类的简单实现,学到了许多操作符和类型转换的东西,记录下来,备忘。。。

Str.h

#include <iostream>
#include <algorithm>
#include <cstring>
#include "Vec.h"

class Str
{
	//友元函数有与成员函数一样的访问权限,而且可以放到类定义中的任意位置
	friend std::istream& operator>> (std::istream&,Str&);
public:
	typedef Vec<char>::size_type size_type;

	Str() { } //默认构造函数
	Str(size_type n,char c):data(n,c) {} //有连个参数(数目,字符)构造数目的字符的字符串
	Str(const char* p){ //从字符串常量转化为string的构造函数
		std::copy(p,p+std::strlen(p),std::back_inserter(data));
		//std::cout<<"测试1"<<std::endl;
	}
	//从迭代器b和e之间的元素创建一个string
	template <class In> Str(In b,In e){
		std::copy(b,e,std::back_inserter(data));
	}

	//operator[]索引操作符
	char& operator[] (size_type i) { return data[i]; }
	const char& operator[] (size_type i) const { return data[i]; }
	
	//连接操作符 ①s=s+s1;②s+=s1
	Str& operator += (const Str& s)
	{
		std::copy(s.data.begin(),s.data.end(),std::back_inserter(data));
		return *this;
	}
	//定义大小
	size_type size() const { return data.size(); }
	
private:
	Vec<char> data;
};

//输入-输出操作符
//判断一个函数时候应该是成员函数,我们前面章节有个标准:看看这个操作是否会影响对象的状态
//输入操作当然改变对象了,应该把它当做Str的成员函数吗?
//不可以,原因:对于一个二元操作符(如cin>>s),左操作数总是与第一个参数绑定,但是对于
//成员操作符函数来说,第一个参数隐式的为此成员函数的对象,所以cin>>s表示的应该是
//cin中得成员函数>>,但是istream是没有定义权限的。所以我们要想使cin>>s成立,就必须
//决定:输入操作符必须是一个非成员函数,同理,输入也是
std::istream& operator>> (std::istream&,Str&);
std::ostream& operator<< (std::ostream&,const Str&);
Str operator + (const Str& ,const Str& );//加法运算符的重载(不会改变任何值,所以类外定义)

Str.cpp

#include "Str.h"
using namespace std;

//我们调用Str::operator[]-->Vec::operator[];类似调用s.size()-->Vec对象的size
ostream& operator<< (ostream& os,const Str& s)
{
	for (Str::size_type i=0;i!=s.size();++i)
	{
		os<<s[i];
	}
	return os;
}
istream& operator>> (istream& is,Str& s)
{
	s.data.clear(); //清空数据
	char c; //一个字符一个字符的读取
	while (is.get(c)&&isspace(c))
	{//一直读取字符,知道不是空字符,或者读取操作结束
		;
	}
	if (is) //判断是否读取结束
	{
		do 
		{
			s.data.push_back(c);
		} while (is.get(c)&&!isspace(c));
		if (is) //判断是否读取结束
		{
			is.unget(); //此时多读取了一个空白字符,要放回到输入流中,防止下次出错
		}
	}
	return is;
}
//加法
Str operator + (const Str& s,const Str& t )
{
	Str rhs=s;
	rhs+=t;
	return rhs;
}

 

另外,再说明两点:

1. 二元操作符问题

+=操作符,改变了它的左操作数,所以应该定义为类Str的成员;

注意:如果一个类支持类型转换,那么把二元操作符定义为非成员函数是一个很好的习惯。

如果一个操作符是一个类的成员,那么这个操作符的左操作数就不能事自动类型转换的结果。

非成员操作符的左操作数以及任意操作符的右操作数,都遵循与普通函数参数相同的规则:操作数可以是任意类型,只要它能转换层参数类型。

+操作符,应该为非成员函数,应为如果是成员函数,左右操作数就不对称了(右操作数可以自动类型转换,左操作数不可以)

+操作符的返回应该是左值,不应该是引用。(c++ primer解释:算术运算符通常产生一个新值,该值是两个操作数的计算结果,它不同于任一操作数,并且在一个局部变量的计算中返回对那个变量的引用是一个运行时错误。)

2. 类型转换操作符(conversion operator)(从类到其他类型,与构造函数相反)

定义:这种操作符可以说明如何把这个类的对象转换为目标类型。类型转换操作符必须被定义为类的成员。格式:operator 目标类型,如 operator double

事实上,我们每次编写隐式的检测一个istream对象的值的循环式,都会使用这种类型转换操作符。如,while(cin)

原理:标准库定义了从istream到void*的类型转换istream operator void*() 。void*可以转化为bool型,所以上例成立。

 

延伸:为什么istream不定义operator bool 直接把cin转换为bool?

考虑 int x; cin<<x;(正确为cin>>x;)  此时,operator bool 直接把cin转换为bool,生成的的bool值会转换成int类型,然后把这个值左移x位,最后丢弃生成的结果。

注:通过定义转换为void*的操作,而不是转换为算术类型,标准库既可以使一个istream对象能用作一个条件式,也可以阻止它被用作一个算术值。

posted @ 2012-03-24 22:50  csqlwy  阅读(2149)  评论(0编辑  收藏  举报