06-C++类和对象强化
一、实现 String 类1.string 类先容
string 类是 c++ 里一个字符串操纵相关的类,简化了一些复杂的字符串操纵。
[*]在C语言中的一些字符串操纵
int main() {
// 定义字符数组并初始化
char str = "hello world";
char str2 = "";
// 字符串拷贝
strcpy(str2, str);
printf("str2 = %s\n", str2);
// 字符串长度
int len = strlen(str2);
printf("str2长度为:%d\n", len);
// 比较字符串大小
char str3 = "hallo world";
int ret = strcmp(str2, str3);
if (ret > 0)
printf("%s 大于 %s\n", str2, str3);
else if (ret == 0)
printf("%s 等于 %s\n", str2, str3);
else
printf("%s 小于 %s\n", str2, str3);
return 0;
}
[*]运行效果
str2 = hello world
str2长度为:11
hello world 大于 hallo world
[*]通过 string 类实现上面的字符串操纵
int main() {
// 定义字符数组并初始化
string str = "hello world";
string str2 = "";
// 字符串拷贝
str2 = str;
cout << "str2 = " << str2 << endl;
// 字符串长度
int len = str2.size();
cout << "str2长度为:" << len << endl;
// 比较字符串大小
string str3 = "hallo world";
if (str2 > str3)
cout << str2 << " 大于 " << str3 << endl;
else if (str2 == str3)
cout << str2 << " 等于 " << str3 << endl;
else
cout << str2 << " 小于 " << str3 << endl;
return 0;
}
[*]运行效果
str2 = hello world
str2长度为:11
hello world 大于 hallo world
[*]说明:上面分别是C语言实现的字符串的根本操纵,以及通过c++ string 类实现的字符串的根本操纵,可以看出,string 类用起来会更加方便。
2.实现本身的 MyString 类
这里实现一些 String 类中比较简朴常用的功能。
2.1 MyString 类的成员
先设计好 MyString 类有哪些成员变量和成员函数。
[*]类的界说
// 定义一个 MyString 类
class MyString
{
friend istream& operator>>(istream &in, MyString &obj);
friend ostream& operator<<(ostream &out, MyString obj);
private:
// 字符串的长度
int size;
// z字符串首元素地址
char *str;
public:
// 构造函数
MyString();
MyString(char *str);
MyString(const MyString &obj);
// 析构函数
~MyString();
// 获取字符串的长度
int Size();
// 重载运算符
char& operator[](int index);
MyString& operator=(MyString &obj);
MyString& operator=(char *str);
bool operator>(MyString &obj);
bool operator==(MyString &obj);
bool operator<(MyString &obj);
bool operator>(char *str);
};
2.2实现 MyString 的拷贝析构
MyString 类的无参构造、有参构造、拷贝构造、析构函数的代码实现。
[*]构造析构实现
MyString::MyString() {
size = 0;
str = NULL;
}
MyString::MyString(char *str) {
size = strlen(str);
// 多加一个放 \0
this->str = new char;
// strcpy 会拷贝 \0
strcpy(this->str, str);
}
MyString::MyString(const MyString &obj) {
size = obj.size;
this->str = new char;
strcpy(str, obj.str);
}
MyString::~MyString() {
if (str != NULL)
{
delete [] str;
str = NULL;
}
}
2.3得到字符串的长度
即得到字符串对象中第一个字符到 \0 之间的字符的个数。
[*]字符串长度
int MyString::Size() {
return size;
}
2.4输出字符串
如果我们直接 cout 输出字符串对象,因为对象中的成员除了字符串首元素地址,还有字符串的长度,因此不能直接输出,须要重载 << 运算符,又因为运算符的左值不是自界说对象,因此要通过全局函数重载 << 运算符,同时别忘了将函数添加友元。
[*]输出字符串:
// 打印字符串,重载 << 运算符
ostream& operator<<(ostream &out, MyString obj)
{
out << obj.str;
return out;
}
2.5输入字符串
键盘获取输入,为对象中成员变量赋值,但是须要实现cin >> 字符串对象,须要重载 >> 运算符。其左值也是一个非自界说对象,因此通过全局函数实现,将全局函数设置为友元。
[*]输入字符串
// 输入字符串,重载 >> 运算符
istream& operator>>(istream &in, MyString &obj)
{
// 如果字符串不为空,先指向空
if (obj.str != NULL)
{
delete [] obj.str;
obj.str = NULL;
}
// 定义一个临时变量存放键盘输入的值
char temp = "";
in >> temp;
obj.size = strlen(temp);
obj.str = new char;
strcpy(obj.str, temp);
return in;
}
[*]说明:
[*]这里的输入不是追加,因为不知道用户具体会输入多少字符,不确定以前的空间是否足够,因此先判断以前的空间是否为 NULL ,开释之前的空间,申请新空间存放字符串;
[*]因为不确定用户输入多少字符串,不知道该申请多大的空间,于是通过一个暂时变量保存字符串,再测字符串长度,根据现实长度申请堆区空间;
[*]这里是要对对象的字符串指针变量写入值,要改变其内容,因此利用引用传递。
2.6操纵单个字符
操纵单个字符,就是通过下标索引,读取或修改字符串中的某个字符,须要重载 [] 运算符。左值为自界说对象,通过类的成员函数实现运算符重载。
[*]重载 []
char &MyString::operator[](int index) {
// 先判断是否为空
if (NULL == str)
{
cout << "字符串为空,无法操作" << endl;
exit(-1);
}
// 判断索引是否越界
if (index >=0 && index <size)
{
return str;
}
else
{
cout << index << "越界" << endl;
exit(-1);
}
}
[*]说明:exit 是直接退出当前历程。
2.7对象给对象赋值
一个对象给另外一个对象赋值,须要重载 = 运算符,同时考虑两种情况,一种是 对象1 = 对象2,另一种是对象 = 字符串。
[*]对象给对象赋值
// 对象赋值给对象
MyString& MyString::operator=(MyString &obj) {
// 先判断左值是否为空,非空要先释放
if (str != NULL)
{
delete [] str;
str = NULL;
}
// 将右变对象的值拷贝给左边对象,深拷贝
size = obj.size;
str = new char;
strcpy(str, obj.str);
return *this;
}
// 将字符串赋值给对象
MyString& MyString::operator=(char *str) {
// 先判断左值是否为空,非空要先释放
if (this->str != NULL)
{
delete [] this->str;
this->str = NULL;
}
// 将右变对象的值拷贝给左边对象,深拷贝
size = strlen(str);
this->str = new char;
strcpy(this->str, str);
return *this;
}
[*]说明:
[*]上面的第一个函数中,如果返回值为引用,那么参数也必须为引用,且第二个参数的返回值也必须为引用,目的是为了保证链式操纵中类型匹配;
[*]假设一种情况 str3 = str2 = str1 = "hello world":会先执行str1 = "hello world",调用上面的第二个函数,然后将赋值后的 str1 的引用返回。此时再执行str2 = str1,将上一次执行的效果作为参数传给第一个函数的 MyString &obj,参数为引用,类型匹配,如果第二个函数返回的是值不是引用,那么这里的链式操纵就完成不了。完成了将 str1 的值赋给 str2 后,会继承返回 str2 的引用,再次调用第一个函数,将上一次执行的效果 str2 的引用传给MyString &obj,如果第一个函数的返回值不是引用,是值的话,这里传参对值取别名会报错,因此返回值和参数的类型必须匹配。
2.8比较字符串对象大小
比较字符串大小,即重载 > < == 运算符。
[*]比较字符串大小
// 重载 > 运算符
bool MyString::operator>(MyString &obj) {
if ((str == NULL) || (obj.str == NULL))
{
cout << "存在空字符串,无法比较" << endl;
exit(-1);
}
if (strcmp(str, obj.str) > 0)
return true;
return false;
}
// 重载 == 运算符
bool MyString::operator==(MyString &obj) {
if ((str == NULL) || (obj.str == NULL))
{
cout << "存在空字符串,无法比较" << endl;
exit(-1);
}
if (strcmp(str, obj.str) == 0)
return true;
return false;
}
// 重载 < 运算符
bool MyString::operator<(MyString &obj) {
if ((str == NULL) || (obj.str == NULL))
{
cout << "存在空字符串,无法比较" << endl;
exit(-1);
}
if (strcmp(str, obj.str) < 0)
return true;
return false;
}
除了比较两个字符串对象的大小,也可能比较一个字符串对象和一个字符串的大小,这里只以重载 > 举例。
[*]比较字符串对象与字符串的大小
bool MyString::operator>(char *str) {
if ((this->str == NULL) || (str == NULL))
{
cout << "存在空字符串,无法比较" << endl;
exit(-1);
}
if (strcmp(this->str, str) > 0)
return true;
return false;
}
[*]说明:比较两个字符串对象大小,以及比较字符串对象和字符串大小时,一定要先判断字符串是否指向 NULL,否则可能访问非法内存,导致段错误。
二、MyString 类完备代码
[*]代码演示
#include <iostream>#include <string.h>using namespace std;// 界说一个 MyString 类class MyString{ friend istream& operator>>(istream &in, MyString &obj); friend ostream& operator<<(ostream &out, MyString obj);private: // 字符串的长度 int size; // z字符串首元素地址 char *str;public: // 构造函数 MyString(); MyString(char *str); MyString(const MyString &obj); // 析构函数 ~MyString(); // 获取字符串的长度 int Size(); char& operator[](int index); MyString& operator=(MyString &obj); MyString& operator=(char *str); bool operator>(MyString &obj); bool operator==(MyString &obj); bool operator<(MyString &obj); bool operator>(char *str);};MyString::MyString() {
size = 0;
str = NULL;
}
MyString::MyString(char *str) {
size = strlen(str);
// 多加一个放 \0
this->str = new char;
// strcpy 会拷贝 \0
strcpy(this->str, str);
}
MyString::MyString(const MyString &obj) {
size = obj.size;
this->str = new char;
strcpy(str, obj.str);
}
MyString::~MyString() {
if (str != NULL)
{
delete [] str;
str = NULL;
}
}
int MyString::Size() {
return size;
}
// 打印字符串,重载 << 运算符
ostream& operator<<(ostream &out, MyString obj)
{
out << obj.str;
return out;
}
// 输入字符串,重载 >> 运算符
istream& operator>>(istream &in, MyString &obj)
{
// 如果字符串不为空,先指向空
if (obj.str != NULL)
{
delete [] obj.str;
obj.str = NULL;
}
// 定义一个临时变量存放键盘输入的值
char temp = "";
in >> temp;
obj.size = strlen(temp);
obj.str = new char;
strcpy(obj.str, temp);
return in;
}
char &MyString::operator[](int index) {
// 先判断是否为空
if (NULL == str)
{
cout << "字符串为空,无法操作" << endl;
exit(-1);
}
// 判断索引是否越界
if (index >=0 && index <size)
{
return str;
}
else
{
cout << index << "越界" << endl;
exit(-1);
}
}
MyString &MyString::operator=(MyString &obj) { // 先判断左值是否为空,非空要先开释 if (str != NULL) { delete [] str; str = NULL; } // 将右变对象的值拷贝给左边对象,深拷贝 size = obj.size; str = new char; strcpy(str, obj.str); return *this;}MyString& MyString::operator=(char *str) { // 先判断左值是否为空,非空要先开释 if (this->str != NULL) { delete [] this->str; this->str = NULL; } // 将右变对象的值拷贝给左边对象,深拷贝 size = strlen(str); this->str = new char; strcpy(this->str, str); return *this;}bool MyString::operator>(MyString &obj) { if ((str == NULL) || (obj.str == NULL)) { cout << "存在空字符串,无法比较" << endl; exit(-1); } if (strcmp(str, obj.str) > 0) return true; return false;}bool MyString::operator==(MyString &obj) { if ((str == NULL) || (obj.str == NULL)) { cout << "存在空字符串,无法比较" << endl; exit(-1); } if (strcmp(str, obj.str) == 0) return true; return false;}bool MyString::operator<(MyString &obj) { if ((str == NULL) || (obj.str == NULL)) { cout << "存在空字符串,无法比较" << endl; exit(-1); } if (strcmp(str, obj.str) < 0) return true; return false;}bool MyString::operator>(char *str) {
if ((this->str == NULL) || (str == NULL))
{
cout << "存在空字符串,无法比较" << endl;
exit(-1);
}
if (strcmp(this->str, str) > 0)
return true;
return false;
}
int main(){ cout << "------------------- 重载<< ------------------" << endl; MyString str1 = "hello world!"; cout << str1 << endl; // 得到字符串的长度 cout << "size = " << str1.Size() << endl; cout << "------------------- 重载>> ------------------" << endl; MyString str2; cout << "请输入一个字符串(不带空格):"; cin >> str2; cout << str2 << endl; cout << "------------------- 重载[] ------------------" << endl; MyString str3 = "hello"; cout << str3 << endl; str3 = 'E'; cout << str3 << endl; cout << "------------------- 重载= ------------------" << endl; MyString str4; MyString str5; str5 = str4 = "hello human"; cout << str4 << endl; cout << str5 << endl; cout << "------------------- 重载比较运算符 ------------------" << endl; MyString str6, str7; cout << "请输入两个字符串:"; cin >> str6 >> str7; if (str6 > str7) cout << str6 << "大于" << str7 << endl; else if (str6 == str7) cout << str6 << "即是" << str7 << endl; else cout << str6 << "小于" << str7 << endl;}
[*]运行效果
------------------- 重载<< ------------------
hello world!
size = 12
------------------- 重载>> ------------------
请输入一个字符串(不带空格):helloworld
helloworld
------------------- 重载[] ------------------
o
hEllo
------------------- 重载= ------------------
hello human
hello human
------------------- 重载比较运算符 ------------------
请输入两个字符串:hallo hello
hallo小于hello
三、其它知识点补充
1.不发起重载 && 和 ||
不要重载 && 和 || ,因为用户无法实现 && 和 || 的短路特性。
[*]代码演示
class Complex {
public:
int flag;
// 构造函数
Complex(int flag) {
this->flag = flag;
}
// 重载 += 运算符
Complex &operator+=(Complex &complex) {
this->flag = this->flag + complex.flag;
return *this;
}
// 重载 && 运算符
bool operator&&(Complex &complex) {
return this->flag && complex.flag;
}
};
int main() {
Complex complex1(0); //flag 0
Complex complex2(1); //flag 1
if (complex1 && (complex1 += complex2)) {
cout << "真!
" << endl;
} else {
cout << "假!" << endl;
}
return 0;
}
[*]运行效果
真!
[*]说明
[*]对象complex1的 flag 变量值为0,按原理来说,执行的效果应该为 "“假!” ,但这里的效果却为 “真!”;
[*]因为在执行if (complex1 && (complex1 += complex2))的时候,(complex1 += complex2)会先触发operator+=(Complex &complex)函数的调用,从而将运算效果 1 赋值给 complex1 的 flag 导致最终的条件为 (1 && 1),因此效果为 “真!”;
[*]所以,我们重载 && 运算符的时候 没法实现其短路特性。
其它不能重载的运算符:. :: .* ?: sizeof。
2.智能指针
智能指针可以帮助我们省略一部分内存操纵,通过在外包裹一层外壳,间接主动实现堆区空间的开释等操纵。
[*]代码演示
class Data {
private:
int num;
public:
Data() {
cout << "Data的无参构造" << endl;
}
Data(int num) {
this->num = num;
cout << "Data的有参构造 num=" << num << endl;
}
void showData() {
cout << "num=" << num << endl;
}
~Data() {
cout << "Data的析构函数" << endl;
}
};
class SmartPoint {
private:
Data *sp;
public:
SmartPoint() : sp(NULL) {}
SmartPoint(Data *sp) {
this->sp = sp;
}
SmartPoint(const SmartPoint &ob) {
this->sp = new Data;
*sp = *ob.sp;
}
~SmartPoint() {
delete sp;
}
Data *operator->() {
return sp;
}
Data &operator*() {
return *sp;
}
};
int main() {
SmartPoint bp(new Data(100));
bp->showData();
(*bp).showData();
return 0;
}
[*]运行效果
Data的有参构造 num=100
num=100
num=100
Data的析构函数
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]