C++-类和动态内存分配-进阶

                                                                                          类和动态内存分配-进阶

(1) 改进后的新String类

新String类将添加以下方法:

int length () const {return len;}

friend bool operator<(const String & st, const String & st2);

friend bool operator>( const String & st1, const String & st2);

friend bool operator==( const String & st, const String & st2);

friend operator>>(istream & is,String & st);

char & operator [] (int i);

const char & operator [] (int i) const;

static int HowMany();

第一个新方法返回被存储的字符串的长度。 接下来的3个友元函数能够对字符串进行比较。 operator>>()函数提供了简单的输入功能;两个operator[]()函数提供了以数组表示法访问字符串中各个字符的功能。 静态类方法HowMany()将补充静态类数据成员num_string。

  1. 修订后的默认构造函数:

请注意新的默认构造函数,它与下面类似:

String::String()

{

len=0;

str=new char[1];

str[0]=’\0’;               //默认string

}

您可能会问,为什么代码为:

str = new char[1];

而不是:

str = new char;

上面两种方式分配的内存量相同,区别在于前者与类析构函数兼容,而后者不兼容。

 

2. 比较成员函数

在String类中,执行比较操作的方法有3个。 如果按字母顺序,第一个字符串在第二个字符串之前,则operator<()函数返回true。 要实现字符串比较函数,最简单的方法是使用标准的strcmp()函数,如果依照字母顺序,第一个参数位于第二个参数之前,则该函数返回一个负值;如果两个字符串相同,则返回0;如果第一个参数位于第二个参数之后,则返回一个正值。 因此,可以这样使用strcmp():

bool operator<(const String &st1, const String &st2)

{

if (std::strcmp(st1.str,st2.str)<0)

return true;

else

return false;

}

因为内置的>运算符返回的是一个布尔值,所以可以将代码进一步简化为:

 

bool operator<(const String &st1, const String &st2)

{

return (std::strcmp(st1.str,st2.str)<0);

}

同样,可以按照下面的方式来编写另外两个比较函数:

bool operator>(const String &st1, const String &st2)

{

return st2<st1;

}

bool operator==(const String &st1,const String &st2)

{

return (std::strcmp(st1.str,st2.str)==0);

}

将比较函数作为友元,有助于将String对象与常规的C字符串进行比较。 例如,假设answer是String对象,则下面的代码:

if(“love”==answer)

将被转换为:

if(operator==(“love”,answer))

然后,编译器将使用某个构造函数将代码转换为:

if(operator==(String(“love”),answer))

这与原型是相匹配的。

 

3. 使用中括号表示法访问字符

对于标准C-风格字符串来说,可以使用中括号来访问其中的字符:

char city[40] = “Amsterdam”;

cout<<city[0]<<endl;    //显式字母A

在C++中,两个中括号组成一个运算符——中括号运算符,可以使用方法operator[]()来重载该运算符。 通常,二元C++运算符(带两个操作数)位于两个操作数之间,例如2+5。 但对于中括号运算符,一个操作数位于第一个中括号的前面,另一个操作数位于两个中括号之间。 因此,在表达式city[0]中,city是第一个操作数,[]是运算符,0是第二个操作数。

假设opera是一个String对象:

String opera(“The Magic Flute”);

则对于表达式opera[4],C++将查找名称和特征标与此相同的方法:

String::operator[](int i)

如果找到匹配的原型,编译器将使用下面的函数调用来替代表达式opera[4]:

opera.operator[](4)

opera对象调用该方法,数组下标4成为该函数的参数。

下面是该方法的简单实现:

char & String::operator[](int i)

{

return str[i];

}

有了上述定义后,语句:

cout<<opera[4];

将被转换为:

cout<<opera.operator[4];

返回值是opera.str[4](字符M)。 由此,公有方法可以访问私有数据。

将返回类型声明为char &,便可以给特定的元素赋值。 例如,可以编写这样的代码:

String means(“might”);

means[0]=’r’;

第二条语句将被转换为一个重载运算符函数调用:

means.operator[][0]=‘r’;

这里将r赋给方法的返回值,而函数返回的是指向means.str[0]的引用,因此上述代码等同于下面的代码:

means.str[0]=’r’;

代码的最后一行访问的是私有数据,但由于operator[]()是类的一个方法,因此能够修改数组的内容。 最终的结果是”might”被改为”right”。

假设有下面的常量对象:

const String answer(“futile”);

如果只有上述operator[]()定义,则下面的代码将出错:

cout<<answer[1];

原因是answer是常量,而上述方法无法确保不修改数据。

但在重载时,C++将区分常量和非常量函数的特征标,因此可以提供另一个仅供const String对象使用的operator[]()版本:

//针对const String对象

const char & String::operator[](int i) const

{

return str[i];

}

有了上述定义后,就可以读/写常规String对象了;而对于const String对象,则只能读取其数据:

String text(“Once upon a time”);

const String answer(“futile”);

cout<<text[1];       //使用非常量版本的operator []()

cout<<answer[1];    //使用常量版本的operator[]()

cin>>text[1];         //使用非常量版本的operator[]()

cin>>answer[1];      //报错

 

4. 静态类成员函数

可以将成员函数声明为静态的,这样做有两个重要的后果。

首先,不能通过对象调用静态成员函数;实际上,静态成员函数甚至不能使用this指针。 如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它。 例如,可以给String类添加一个名为HowMany()的静态成员函数,方法是在类声明中添加如下原型/定义:

static int HowMany()  {return num_strings;}

调用它的方式如下:

int count = String::HowMany();    //调用静态成员函数

其次,由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。 例如,静态方法HowMany()可以访问静态成员num_strings,但不能访问str和len。

 

5. 进一步重新赋值运算符

假设要将常规字符串复制到String对象中。 例如,假设使用getline()读取了一个字符串,并要将这个字符串放置到String对象中,前面定义的类方法让您能够这样编写代码:

String name;

char temp[40];

cin.getline(temp,40);

name = temp;      //使用构造函数来转换类型

最后一条语句工作原理:

  • 程序使用构造函数String(const char *)来创建一个临时String对象,其中包含temp中的字符串副本。
  •  String & String::operator=(const String &)函数将临时对象中的信息复制到name对象中。
  • 程序调用析构函数~String()删除临时对象。

为提高处理效率,最简单的方法是重载复制运算符,使之能够直接使用常规字符串,这样就不用创建和删除临时对象了。 下面是一种可能的实现:

String & String::operator=(const char * s)

{

delete [] str;

len=std::strlen(s);

str=new char[len+1];

std::strcpy(str,s);

return *this;

}

一般来说,必须释放str指向的内存,并未新字符串分配足够的内存。

 

6. 程序清单

//string1.h -- 修正并加强后的String类定义
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#ifndef STRING1_H_
#define STRING1_H_
using std::ostream;
using std::istream;
class String
{
private:
	char * str;      //指向字符串的指针
	int len;        //字符串的长度
	static int num_strings;    //对象个数
	static const int CINLIM = 80;    //cin输入上限
public:
	//构造函数和其他方法
	String(const char * s);   //构造函数
	String();                 //默认构造函数
	String(const String &);   //复制构造函数
	~String();                //析构函数
	int length() const { return len; }
	//重载运算符方法
	String & operator=(const String &);
	String & operator=(const char *);
	char * operator[](int i);
	const char & operator[](int i) const;
	//重载运算符友元
	friend bool operator<(const String & st, const String & st2);
	friend bool operator>(const String & st1, const String & st2);
	friend bool operator==(const String & st, const String & st2);
	friend ostream & operator<<(ostream & os, const String & st);
	friend istream & operator<<(istream & is, String & st);
	//静态函数
	static int HowMany();
};
#endif // !STRING1_H_
//string1.cpp -- String类方法
#define _CRT_SECURE_NO_WARNINGS
#include<cstring>
#include"string1.h"
using std::cin;
using std::cout;

//初始化静态类成员
int String::num_strings = 0;

//静态方法
int String::HowMany()
{
	return num_strings;
}
//类方法
//以C字符型构造String
String::String(const char * s)
{
	len = std::strlen(s);        //设置长度
	str = new char[len + 1];     //分配内存
	std::strcpy(str, s);         //初始化指针
	num_strings++;               //设置对象数量
}
String::String()            //默认构造函数
{
	len = 4;
	str = new char[4];
	str[0] = '\0';            //默认字符串
	num_strings++;
}
String::String(const String &st)
{
	num_strings++;           //处理静态成员更新
	len = st.len;            //相同长度
	str = new char[len + 1];  //分配空间
	std::strcpy(str, st.str); //将字符串复制到新的位置
}
String::~String()           //必要的析构函数
{
	--num_strings;               //有要求
	delete[] str;                 //有要求
}
//重载运算符方法
//将一个String赋给另一个String
String & String::operator=(const String & st)
{
	if (this == &st)
		return *this;
	delete[] str;
	len = st.len;
	str = new char[len + 1];
	std::strcpy(str, st.str);
	return *this;
}
//将一个C String赋给另一个String
String & String::operator=(const char *s)
{
	delete[] str;
	len = std::strlen(s);
	str = new char[len + 1];
	std::strcpy(str, s);
	return *this;
}
//对于非常量String有读写权限
char & String::operator[](int i)
{
	return str[i];
}
//对于常量String只读权限
const char & String::operator[](int i) const
{
	return str[i];
}
//重载运算符友元
bool operator<(const String &st1, const String &st2)
{
	return (std::strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String &st1, const String &st2)
{
	return st2 < st1;
}
bool operator==(const String &st1, const String &st2)
{
	return(std::strcmp(st1.str, st2.str) == 0);
}
//简单的String输出
ostream & operator<<(ostream & os, const String & st)
{
	os << st.str;
	return os;
}
//快速输入
istream & operator>>(istream & is, String & st)
{
	char temp[String::CINLIM];
	is.get(temp, String::CINLIM);
	if (is)
		st = temp;
	while (is&&is.get() != '\n')
		continue;
	return is;
}
//sayings1.cpp -- 使用拓展的String类
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include"string1.h"
const int ArSize = 10;
const int MaxLen = 81;
int main()
{
	using std::cout;
	using std::cin;
	using std::endl;
	String name;
	cout << "Hi, what's your name?\n";
	cin >> name;
	cout << name << ", please enter up to " << ArSize
		<< " short sayings <empty line to quit>:\n";  
	String sayings[ArSize];                   //对象数组              
	char temp[MaxLen];                        临时字符串存储
	int i;
	for (i = 0; i < ArSize; i++)
	{
		cout << i + 1 << ": ";
		cin.get(temp, MaxLen);
		while (cin&&cin.get() != '\n')
			continue;
		if (!cin || temp[0] == '\0')     //空行?
			break;                       //i不自增
		else
			sayings[i] = temp;           //重载赋值
	}
	int total = i;                       //读到行数总和
	if (total > 0)
	{
		cout << "Here are your sayings:\n";
		for (i = 0; i < total; i++)
			cout << sayings[i][0] << ": " << sayings[i] << endl;
		int shortest = 0;
		int first = 0;
		for (i = 1; i < total; i++)
		{
			if (sayings[i].length() < sayings[shortest].length())
				shortest = i;
			if (sayings[i] < sayings[first])
				first = i;
		}
		cout << "Shortest saying:\n" << sayings[shortest] << endl;
		cout << "First alphabetically:\n" << sayings[first] << endl;
		cout << "This program used " << String::HowMany()
			<< " String objects. Bye.\n";
	}
	else
		cout << "No input! Bye.\n";
	cin.get();
	return 0;
}

该程序中,程序要求用户输入至多10条谚语。 每条谚语都被读到一个临时字符数组,然后被复制到String对象中。 显式用户的输入后,程序使用成员函数length()和operator<()来确定最短的字符串以及按字母顺序排列在最前面的字符串。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值