String类的编写
// class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
#include<iostream>
using std::istream;
using std::ostream;
class String
{
private:
char* str; //字符串指针,此处没有分配内存,后续用new来为其开辟内存空间
int len;
static int num_strings; //只有static,则必须声明后在实现文件中进行初始化
static const int CINLIM = 80; //const static 可以直接在类中初始化
public:
String(const char* s);
String();
String(const String& s);//赋值构造函数
~String(); //在使用new的类中十分重要 -- 配套使用delete来释放堆空间
int length() const
{
return len;
};
String& operator=(const String&st); //对象赋值
String& operator=(const char*s); //一般字符串赋值
char& operator[](int i); //重载 中括号[] 运算符 //可改变数据版本
const char& operator[](int i)const; //不改变数据版本
friend bool operator<(const String& st1, const String& st2);
friend bool operator>(const String& st1, const String& st2);
friend bool operator==(const String& st1, const String& st2);
friend istream& operator>>(istream &is, String& st); // cin>>
friend ostream& operator<<(ostream &os, const String& st); // cout<<
//静态函数
static int HowMany();
};
#endif
// fuc.cpp
#include<cstring> // strcpy--复制字符串, strcmp--根据ASCII码值比较字符串,strlen--获取字符串的长度
#include"class.h"
using namespace std;
//为类中声明的static变量初始化,且要声明其所在的类区域
int String::num_strings = 0;
int String::HowMany()
{
return num_strings;
}
String::String(const char* s)
{
len = std::strlen(s); //strlen获取的是字符串除去末尾空字符的长度,所以长度要为 len+1;
str = new char[len + 1]; //在构造函数中使用new申请堆空间,在析构函数中使用delete释放堆空间
strcpy(str, s);
num_strings++;
}
String::String()
{
len = 4;
str = new char[len + 1];
str[0] = '\0'; //设置为空指针,也可为设置为 NULL, nullptr,(void *)0q
num_strings++;
}
String::String(const String& s)//复制构造函数
{
num_strings++; //为新初始化的对象复制,所以可以在总数上加上1
len = s.len;
//深度复制:
str = new char[len + 1];
strcpy(str, s.str);
}
String::~String() //在使用new的类中十分重要 -- 配套使用delete来释放堆空间
{
--num_strings;
delete[] str;
}
String& String::operator=(const String& st) //对象赋值
{
if (this == &st) //如果调用对象和参数已经相同
{
return *this;
}
delete[] str; //先删除之前的内存,防止内存的浪费
len = st.len;
//深度复制
str = new char[len + 1];
strcpy(str, st.str);
return *this; //使得可以进行连续复制操作
}
String& String::operator=(const char* s) //一般字符串赋值
{
delete[] str;
len = strlen(s);
str = new char[len + 1];
strcpy(str, s);
return *this;
}
char& String::operator[](int i) //重载 中括号[] 运算符 //可改变数据版本
{
return str[i]; //即返回调用对象字符串的第i个字符
}
const char& String::operator[](int i)const//不改变数据版本
{
return str[i];
}
//友元函数:
bool operator<(const String& st1, const String& st2)
{
return (strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String& st1, const String& st2)
{
return st2 < st1; //直接使用上面刚重载的运算符 逻辑:若语句为: st1>st2,若成立则应当有st2<st1,可以实现函数功能
//或 return (strcmp(st1.str,st2.str)>0)
}
bool operator==(const String& st1, const String& st2)
{
return (strcmp(st1.str, st2.str) == 0);
}
istream& operator>>(istream& is, String& st) // cin>>
{
char temp[String::CINLIM]; //友元函数,不能直接使用类中的静态常量,需要使用作用域解析运算符
is.get(temp, String::CINLIM); // 相当于cin,get(name,size)
if (is)
{
st = temp; //使用的重载过的对于常规字符串的赋值运算符
}
//while的作用:读取掉剩下的多余出来的字符
while (is && is.get() != '\n') // 若输入失败,则istream对象is的值将被置为false;
{
continue;
}
return is;
}
ostream& operator<<(ostream& os, const String& st) // cout<<
{
os << st.str;
return os;
}
// main.cpp
#include<iostream>
#include"class.h"
const int Arsize = 10;
const int MaxLen = 81;
int main()
{
using namespace std;
String name;
cout << "Hi,what is your name? " << endl;
cin >> name; //本质: cin为第一个参数,name为第二个参数
cout << name << ", please enter up to" << Arsize
<< " short sayings <empty line to quit>:"
<< endl;
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') //实现 empty to quit
{
break;
}
else
{
sayings[i] = temp;
}
}
int total = i; //记录输入的总数
if (total > 0)
{
cout << "Here are your saying: " << endl;
for (i = 0; i < total; i++)
{
//sayings[i][0] sayings[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]) //比较出ASCII值最小的字符串
{
first = i;
}
}
cout << "shortest sayings: " << sayings[shortest] << endl;
cout << "First alphabetically: " << sayings[first] << endl;
cout << "This program used :" << String::HowMany()
<< " strings objects. Bye~" << endl;
}
else
{
cout << "No input! Bye~" << endl;
}
system("pause");
return 0;
}
知识点:
1.strcmp(s1,s2)的详细内容:
1):原型:int strcmp(const char *s1, const char *s2); 头文件:#include <string.h>
2):若s1>s2,则返回一个大于零的值,若s1=s2(字符串完全相同),则返回一个等于零的值,若s1<s2,则返回一个小于零的值
3):说明:strcmp()函数是根据ACSII码的值来比较两个字符串的;strcmp()函数首先将s1字符串的第一个字符值减去s2第一个字符,若差值为零则继续比较下去;若差值不为零,则返回差值。
测试代码如下:
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
const char* a = "abcd";
const char* b = "aaaa";
const char* c = "aaaa";
cout << strcmp(a, b) << endl; //返回值为1,说明a>b,所以当两个字符串长度相等时,会继续根据ASCII码值进行比较
cout << strcmp(b, c) << endl; //只有两个字符串完全相等时,返回值才是0
const char *L1 = "c fool and his money are soon parted";
const char* L2 = "penny wise,pound foolish";
cout << strcmp(L1, L2) << endl; //返回值为-1,说明是strcmp是根据ASCII码值来比较大小的
system("pause");
return 0;
}
2.strcmp是用来比较两个字符串的ASCII码值的大小的,若想要比较两个字符串的长度,可以先用strlen()或者string.length()来获取字符串的长度,再进行相应的比较。
3.若类中包含了使用new初始化的指针成员,则应定义一个复制构造函数( 参数为类对象引用的构造函数)以及重载赋值运算符,来实现深度复制(即复制数据的值)。
4.实现深度复制:给指针分配一个新的内存,再把值赋给指针 -- 即实现了值的复制
1): (char * )str = new char[n] 2): strcpy(str , st.str)
5.空指针的表示方法: str = '\0' = NULL = nullptr = (void *) 0
6.静态类成员函数:在类中的函数声明前加上static -- 不能通过对象访问,而使用类名和作用域解析运算符' :: '来访问。(在实现文件中定义时不用再加上static--与友元函数形式类似)
7.复制构造函数在为新创建的对象初始化时被调用。
8.类中声明的static变量:若为const static,则可以在类中直接初始化,若只是static, 则必须在类中声明后,再去实现文件中进行初始化。(初始化不用再加上static,且要声明其所属于的类)
9. 为类中静态变量初始化:
1):在类中:static int num_strings;
2):在实现文件中:int String::num_strings = 0;