133-C++学习第七弹(迭代器,重载,string)

本文介绍了C++实现的学生信息管理系统,包括添加、查询、删除学生功能,使用了STL库和迭代器。讲解了迭代器的使用、重载运算符以及深拷贝和移动构造函数的概念,同时探讨了字符串操作的注意事项,如头文件的选择和内存管理。
摘要由CSDN通过智能技术生成

注意
C++不带.h的是C++的stl库
如< string >是字符串对象的库
<string.h> < cstring >是字符串的函数库,俗称老库
using namespace std;//命名空间

引用返回的好处:
在这里插入图片描述
静态成员函数
可以拿类型名加四个点直接访问静态成员
也可以拿对象直接访问静态成员
在这里插入图片描述
但是下面这个形式
在这里插入图片描述
上面的访问NUM代表静态,下面的Node是Obj嵌套类型
编译器识别区分不出了
必须拿关键字
在这里插入图片描述
typename类型名关键字
让编译器知道Node
在这里插入图片描述

void Pritn_Student() const
{
	typename std::vector<Student>::const_iterator it = studdata.begin();
	for (; it != studdata.end(); ++it)
	{
		cout << it->Id() << " " << it->Name() << " " << it->Sex() << " " << it->Age() << endl;
	}
	cout << endl;
}

迭代器
在这里插入图片描述
常迭代器begin,指向第一个学生,end()返回最后一个学生的后继,两个it不相等,调动对象的id,Name,Sex,Age输出,然后++it, 最后这两个迭代器指到一块了,到末尾了。
在这里插入图片描述
auto特点
for(auto x:studdata)

for循环的auto的能力:可以获取studdata容器存储数据的类型,x就是Student类型了,输出的时候,把s1对象拷贝到x上,x就不是指针了。整个是拷贝构造的动作
但是这句代码效率非常低 产生大规模的拷贝
最好的方法如下

void Pritn_Student() const//打印学生//常方法
	{
		
		cout << "output 学生信息" << endl;
		for(auto const  &x: studdata)//常引用
		{
			x << cout;
			cout << endl;
		}
		cout << endl;
	}

此处用到重载<<
重载<<如下:

ostream& operator<<(ostream& out) const
{
	out << s_id << " " << s_name << " " << s_sex << " " << s_age;
	return out;
}

在这里插入图片描述
在这里插入图片描述

学生信息管理系统的实现(不完整)

#include<string>
#include<vector>
#include<iostream>
using namespace std;
class Student//学生信息
{
private:
	string s_id;//学号
	string s_name;//姓名
	string s_sex;//性别
	int    s_age;//年龄
public:
	Student() :s_age(0) {}//缺省构造函数,不带参数
	Student(const string& id, const string& name, const string& sex, const int age)//构造函数//引用带进来,且不需修改
		:s_id(id), s_name(name), s_sex(sex), s_age(age)//构建,带参数的构造函数
	{}

	~Student() {}//析构函数

	//赋值函数和拷贝构造函数暂时不需写

	string & Id() { return s_id; }//引用返回,使函数具有双重特性,即可以获取学生id号又可以修改它
	const string& Id() const { return s_id; }//常对象调用此方法//使得程序具有通用性

	string& Name() { return s_name; }//普通对象调用普通方法,没有普通方法就调用常方法
	const string& Name() const { return s_name; }//常对象,不能赋值,可以取值

	string& Sex() { return s_sex; }
	const string& Sex() const { return s_sex; }

	int& Age() { return s_age; }
	const int& Age() const { return s_age; }
	//通过公有方法改变私有属性

	ostream& operator<<(ostream& out) const//重载输出流
	{
		out << s_id << " " << s_name << " " << s_sex << " " << s_age;
		return out;
	}
};


class Course//课程类型
{
private:
	string 	c_id;//课程号
	string  c_name;//课程名
	string  c_thacher;//任课老师
	int     credit;//学分
};
class Score
{
private:
	string s_id;//学号
	string c_id;//课程号
	float  result;//成绩
};

class StudManage//学生个人信息类实现好之后,要把学生信息放入容器中
{
	std::vector<Student> studdata;//学生数值,数组,容器
	typename std::vector<Student>::iterator cur;//定义:当前迭代器,typename告诉编译器iterator是类型,不是静态成员
public:
	StudManage() {}//缺省构造函数
	~StudManage() {}//析构函数

	void Add_Student(const Student& s)//添加学生
	{
		studdata.push_back(s);//尾部添加,vector是数组,尾部添加比较好
	}

	void Pritn_Student() const//打印学生//常方法
	{
		
		cout << "output 学生信息" << endl;//输出学生信息
		for(auto const  &x: studdata)//常引用
		{
			x << cout;//使用重载<<
			cout << endl;//换行
		}
		cout << endl;//换行
	}

	bool Find_Id(const string& id)//查询功能的实现,普通方法,因为后期其他功能需要使用
	{
		bool res = false;//为了返回bool值,初始化为假
		cur = studdata.begin();//指向第一个元素
		for (; cur != studdata.end(); ++cur)//使用迭代器查询
		{
			if (cur->Id() == id)
			{
				cout << "查询成功" << endl;
				res = true;//定义为真
				break;//找到了
			}
		}
		return res;//返回真假,根据返回值即可知道是否查询成功
	}

	bool Del_Student_Id(const string& id)//删除学生功能的实现,根据id号删除
	{
		bool res = Find_Id(id);//判断是否查找成功
		if (res)//如果查询成功
		{
			studdata.erase(cur);//迭代器功能,删除了数据,迭代器失效了
			cur = studdata.end();//迭代器失效了,让指向末尾
		}
		return res;//返回真假,是否成功删除
	}
};

void Add(StudManage& studMan)//添加函数
{
	string id, name, sex;
	int age;
	char ch;
	do
	{
		cout << "请入学生: " << endl;
		cin >> id >> name >> sex >> age;
		Student s(id, name, sex, age);
		studMan.Add_Student(s);
		cout<<"是否继续(N/Y)"<<endl;
		cin >> ch;
	}while(ch == 'y' || ch == 'Y');
	cout << "添加完成" << endl;
}

bool Find_Student(StudManage& studMan)//查询函数,对容器查询
{
	string id;
	cout << "input find student id" << endl;//请输入学生学号
	cin >> id;//输入学号
	return studMan.Find_Id(id);//调用,学号查询
}

bool Del_Student(StudManage& studMan)//删除函数
{
	string id;
	cout << "input del student id" << endl;//请输入删除学生的学号
	return studMan.Del_Student_Id(id);//调用
}

int MenuStudent()//定义菜单
{
	int pos = 0;
	cout << "*************************" << endl;
	cout << "*1.Add           2.Print*" << endl;
	cout << "*3.Find          4.Del  *" << endl;
	cout << "*5.Updata        0.退出 *" << endl;
	cout << "*************************" << endl;
	cout << " 请选择" << endl;
	cin >> pos;
	return pos;
}

int main()//主函数调用,实现学生信息管理系统
{
	StudManage studMan;

	int pos = 0;//菜单
	do
	{
		pos = MenuStudent();//学生菜单
		switch (pos)
		{
		case 0: cout << "退出" << endl;
		case 1: Add(studMan); break;
		case 2: studMan.Pritn_Student(); break;
		case 3:
			if (Find_Student(studMan))
			{
				cout << "查询成功" << endl;
			}
			else
			{
				cout << "查询失败" << endl;
			}
			break;
		case 4: if(Del_Student(studMan))
				{
					cout << "删除成功" << endl;
				}
				else
				{
					cout << "删除失败" << endl;
				}
			break;
		case 5:
			if (Find_Student(studMan))
		default:
			cout << "选择错误!" << endl;
		}
	} while (pos != 0);//条件

	return 0;
}

重点

class String
{
private:
	char* str;
	String(char* sp, int)
	{
		str = sp;
	}
public:
	String(const char* s = nullptr)//构造函数 
	{
		if (s != nullptr)
		{
			int len = strlen(s);
			str = new char[len + 1];
			strcpy_s(str, len + 1, s);
		}
		else
		{
			str = new char[1];
			str[0] = '\0';
		}
	}
	
	~String()//析构函数 
	{
		if (str != nullptr)
		{
			delete[]str;
		}
		str = nullptr;
	}
}

深拷贝函数

String(const String& s)//深拷贝 
{
	int len = strlen(s.str);
	str = new char[len + 1];
	strcpy_s(str, len + 1, s.str);
}

深赋值语句重载函数

String& operator=(const String& s)//深赋值语句的重载 
{
	if (this != &s) 
	{
		delete[]str;//释放自己的资源 
		int len = strlen(s.str);//计算要赋值的字符串的长度 
		str = new char[len + 1];//申请新的空间 
		strcpy_s(str, len + 1, s.str);//拷贝 
	}
	return *this;
	//s1 = s2;
	//  s1.str = null  s2.str= null;
	//  s1.str = null  s2.str != null;
	//  s1.str != null, s2.str == null;
	//  s1.str != null , s2.str != null
	
}

移动构造函数

String(String&& s) //移动构造 
	{
		str = s.str;
		s.str = nullptr;//拥有权的转移 
	}

移动赋值函数

	String& operator=(String&& s)//移动赋值 
	{
		if (this != &s)
		{
			delete[]str;//释放资源 
			str = s.str;//指向s.str所拥有的 
			s.str = nullptr;//s.str置为空 
		}
		return *this;
	}

在这里插入图片描述
上面这个程序问题出在哪里呢?
s1+s2;发生内存泄漏
构建对象,无论如何str都不为空,最差情况都会含一个\0
减轻编写的压力
在这里插入图片描述
return String(sp);
这里调动的是构造函数
因为sp是指针类型 不是右值对象
在这里插入图片描述
sp是构造它
构造函数的s指向sp指向的“tulunhello”,s不为空,产生len长度,构建对象,(就是临时对象),内容拷贝进去,返回。但是sp指向的对象还在。“tulunhello"有两个备份。构建的这个对象是将亡值,调用移动赋值。不相等,把s3的str析构掉,把s3的str指向将亡值的这个成员,“tulunhello”,然后把将亡值变为空,return *this,赋值结束,调用析构函数,总的来说就是sp构建的对象没有被析构掉。
更改:

String operator+(const String& s) const
{
	int alen = strlen(this->str); // *this
	int blen = strlen(s.str); 	  // s;
	char* sp = new char[alen + blen + 1];
	strcpy_s(sp, alen + 1, this->str);
	strcat_s(sp, alen + blen + 1, s.str);
	String tmp(sp);
	delete[]sp;
	return tmp;
}

在这里插入图片描述
地址不一样。
析构两次:tmp对象的析构,tmp将亡值对象的析构

私有构造

private:
	char* str;
	String(char* sp, int)
	{
		str = sp;
	}
String operator+(const String& s) const//s1 s2相加本身不能改变,结果给s3 
	{
		int alen = strlen(this->str); // *this
		int blen = strlen(s.str); 	  // s
		char* sp = new char[alen + blen + 1];//总长度 
		strcpy_s(sp, alen + 1, this->str);//给前者(this)先拷贝 
		strcat_s(sp, alen + blen + 1, s.str);//后者(s.str)拷贝进去 
		return String(sp, 1);//sp调用构造函数 
		//无名对象(右值),调用移动赋值函数 
	}

当我们构建s1,s2,s3对象,s1和s2相加,char* sp = new char[alen + blen + 1];//总长度 动态产生了空间,构建临时对象,临时对象的str指向sp,sp获得的拥有权给当时的临时对象(无名对象),再把无名对象给s3赋值,移动赋值,s3的str指向sp指向的空间,空间转移了。调用析构函数。针对于赋值语句写的私有构造函数

String operator+(const String& s) const
{
	int alen = strlen(this->str); // *this
	int blen = strlen(s.str); 	  // s;
	char* sp = new char[alen + blen + 1];
	strcpy_s(sp, alen + 1, this->str);
	strcat_s(sp, alen + blen + 1, s.str);
	String tmp;
	delete[]tmp.str;
	tmp.str = sp;
	return tmp;
	//return String(sp, 1);
}

对象和字符串相加

String operator+(const char* s) const
{
	return *this + String(s);
}

字符串和对象相加

String operator+(const char* s, const String& st)
{
	return  String(s) + st;
}

下面在string类里面进行重载<<

ostream& operator<<(ostream& out) const//实现s3<<cout
{
	out << this->str;
	return out;
}

下面这个重载函数放在外部,没有this指针,两个参数,cout和s3传进去,然后参数s3调用它的类(String)里的重载函数,也就是调用第一个重载<<函数。

ostream& operator<<(ostream& out, const String& s)//实现cout<<s3<<endl;
{
	s << out;
	return out;
}

在这里插入图片描述

int main()
{
	String s1("tulun");
	String s2("hello");
	String s3;

	s3 = s1 + s2;
	s3 = s1 + "newdata";
	s3 = "newdata" + s1;
	//s3 << cout;
	cout << s3 << endl;

	return 0;
}

int main()
{
	String s1("yhping");
	String s2(std::move(s1));//移动 
	String s3("hello");
	s3 = s1;
	
	return 0;
}

重载<<的应用例子:

#include<iostream>
using namespace std;
class String
{
private:
	char* str;
	String(char* sp, int)
	{
		str = sp;
	}
public:
	String(const char* s = nullptr)//构造函数 
	{
		if (s != nullptr)
		{
			int len = strlen(s);
			str = new char[len + 1];
			strcpy_s(str, len + 1, s);
		}
		else
		{
			str = new char[1];
			str[0] = '\0';
		}
	}

	~String()//析构函数 
	{
		if (str != nullptr)
		{
			delete[]str;
		}
		str = nullptr;
	}
	String(const String& s)//深拷贝 
	{
		int len = strlen(s.str);
		str = new char[len + 1];
		strcpy_s(str, len + 1, s.str);
	}
	ostream& operator<<(ostream& out) const//实现s3<<cout
	{
		out << this->str;
		return out;
	}

};
ostream& operator<<(ostream& out, const String& s)//实现cout<<s3<<endl;
{
	s << out;
	return out;
}

int main()
{
	String s1("tulun");
	s1<< cout;
	cout << s1<< endl;

	return 0;
}

运行截图如下
在这里插入图片描述

String& operator+=(const String& s) 
{
	int alen = strlen(this->str); // *this
	int blen = strlen(s.str); 	  // s;
	char *sp = new char[alen + blen + 1];
	strcpy_s(sp, alen + 1, this->str);
	strcat_s(sp, alen + blen + 1, s.str);
	delete[]this->str;
	this->str = sp;
	return *this;
}

String& operator+=(const char* s)
{
		return *this += String(s);
}

bool operator==(const String& _X) const
{		
    return strcmp(this->str, _X.str) == 0;
}	// s1 == s2; s1.operator=(s2);

bool operator!=(const String& _X) const
{
	return !(*this == _X);
}

int main()
{
	String s1("tulun");
	String s2("hello");
	s1 += s2;
	s1 += "yhping";
	s1 == s2;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值