修订后的各个函数
知道如何使用复制构造函数和赋值运算符就能正确管理内存的new和delete,就不用在监视构造函数和析构函数了.如何使管理内存和类.
创建一个String类:
class String
{
private:
char * str; // 指向字符串的指针
int len; // 字符串长度
static int num_strings; // String类对象个数
static const int CINLIM = 80; // 每个String对象限制输入的字符
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();
};
默认构造函数
默认构造函数String(),只需要将长度len = 0 ,new出一段空间存放’\0’的值,表示的就是当String类创造一个对象没有进行任何赋值操作的时候,会将这个对象初始化为空字符串.
标准strcmp()函数
strcmp()的原型为int strcmp(const char *s1,const char *s2),通过各个字符的ascll码值得比较来比较字符串的大小,比如"app"和"bpp"就是"app"的第一个字符a比"bpp"中的第一个字符b小,所以返回负值,若相同则往下比较直到碰见’\0’结束,如果全部相同则表示相等,返回0,若在比较过程中出现相应位置的字符比另一个字符大,则返回1.
各个函数定义如下:
int String::num_strings = 0; //静态成员初始化
// 静态类成员函数
// 静态函数的声名带static关键字,但是定义不带
// 静态成员不能够使用this指针
// 若静态成员函数是在公有部分声名的,可以使用域解析运算符直接调用
// 静态成员函数不与特定的对象关联,静态成员函数只能够使用静态成员函数
int String::HowMany()
{
return num_strings;
}
//带参数的构造函数,可作为强制转换,可以直接赋值给String对象,通过这个构造函数String(const char * s).严格意义来上说,"字符串"是一个地址常量,是不能改变字符串中的值,所以这里的参数是const类型的,但是类中的str所指空间是new出来的,相当于一个字符数组,所以说可以改变值.
String::String(const char * s)
{
len = std::strlen(s); // 设置大小
str = new char[len + 1]; // allot storage
std::strcpy(str, s); // initialize pointer
num_strings++; // set object count
}
String::String() // 默认构造函数
{
len = 0;
str = new char[1]; //这条语句与str = new char这条语句表达的意思相同,但是不能和析构函数中的delete [] str匹配.
str[0] = '\0'; // 调用默认构造函数的都是一个空字符串
num_strings++;
}
//复制构造函数
String::String(const String & st)
{
num_strings++; // handle static member update
len = st.len; // same length
str = new char [len + 1]; // allot space
std::strcpy(str, st.str); // copy string to new location
}
String::~String() // necessary destructor
{
--num_strings; // required
delete [] str; // required
}
// 运算符重载函数
// 重写赋值构造函数
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;
}
// 用于强制转换,一个数组char temp[40]中的数组名temp可以直接赋值给对象
/*****************************
String name; //默认构造函数已经初始化了
char temp[40];
cin.getline(temp ,40 ); //输入整行字符
name = temp; //相当于调用nmae.operator=(temp),temp是char*.
*****************************/
String & String::operator=(const char * s)
{
delete [] str; //删除已经初始化的str 的空间
len = std::strlen(s); /****
str = new char[len + 1]; 这三句都是将s所指字符串的值进行深度复制
std::strcpy(str, s); ****/
return *this; //返回调用函数对象
}
// 可以通过某个对象直接访问其中的字符,比如food是String的对象,food[0]可以直接调用[]这个运算符的重载函数,而且因为返回值类型是char& ,所以可以直接给food[0]重新赋值,比如
String = food("might"),food[0] = 'r',第二条语句就是调用operator[](int i)这个重载函数,char& food.operator[](0)其实就是food.str[0]的引用,所以最后就改变了food.str[0]='r'的值.
此方法不保证对象改变.
char & String::operator[](int i)
{
return str[i];
}
// 增加了一个返回常量对象中某个字符的方法
const char & String::operator[](int i) const
{
return str[i];
}
// 友元函数
// String对象之间比较有助于与常规字符串进行比较,比如,"love" == answer就会调用友元函数operator==(const String &st1, const String &st2),因为"love"通过构造函数String(const char * s) 可以进性强制转换,变为String("love").
bool operator<(const String &st1, const String &st2)
{
return (std::strcmp(st1.str, st2.str) < 0); //若strcmp(st1.str, st2.str) < 0则返回1,否则返回0
}
bool operator>(const String &st1, const String &st2)
{
return st2 < st1; //调用友元函数,st1>st2就是st2<st1,所以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;
}
// 给String通过cout >> 直接赋值
istream & operator>>(istream & is, String & st)
{
char temp[String::CINLIM]; //有限制,字符大小最多为80
is.get(temp, String::CINLIM); //输入一一整行字符<80
if (is) //输入正确
st = temp; //调用st.operator=(temp)重载函数
while (is && is.get() != '\n') 丢弃多余的字符
continue;
return is;
}
主函数执行如下:
#include <iostream>
#include "string1.h"
const int ArSize = 10;
const int MaxLen =81;
// main()中完成的就是输入及条语句放在临时数组temp[]中,然后在赋值给String数组对象的成员,遇到空行或者输入错误就会跳出然后打印.
int main()
{
using std::cout;
using std::cin;
using std::endl;
String name;
cout <<"Hi, what's your name?\n>> ";
cin >> name; //调用operator>>(istream & is, String & st)重载函数
cout << name << ", please enter up to " << ArSize
<< " short sayings <empty line to quit>:\n";
String sayings[ArSize]; // String对象数组
char temp[MaxLen]; // 临时字符串存储
int i; //计数
for (i = 0; i < ArSize; i++)
{
cout << i+1 << ": ";
cin.get(temp, MaxLen); //将输入的字符串存储到temp存储的
while (cin && cin.get() != '\n') //丢弃多余的字符
continue;
if (!cin || temp[0] == '\0') // 输入不正确或者什么都没有输入
break; // 退出输入队列循环
else
sayings[i] = temp; // 输入正确,调用sayings[i].operator=(temp)
}
int total = i; // total # of lines read
if ( total > 0) 输出结果,输入的
{
cout << "Here are your sayings:\n";
for (i = 0; i < total; i++)
cout << sayings[i][0] << ": " << sayings[i] << endl;
//sayings[i][0]的意思是的sayings[i]第i个String对象调用了[]重载运算符,也就是调用了sayings[i].operator[](0),也就是该String对象中的第一个字符
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";
return 0;
}
运行结果如下: