面向对象上机,函数参数里面最常用到的应该就是字符串了,但是一直都没有搞清楚具体有哪些使用形式,怎么用,对使用时所会发生的错误有哪些解决办法?于是乎在今天做个总结,方便自己日后回顾。
Ⅰ.字符指针
1.常见错误与处理方法
(1)E0167 “const char *” 类型的实参与 “char *” 类型的形参不兼容
比如下面这段代码
/*定义student类,包含姓名,学号,分数,三个数据成员;有三个成员函数:设置学生信息,显示学生信息,
比较两个学生信息是否完全相同(可以使用一个对象,或一个对象指针,或一个对象对象引用,作为形参);
为student类编写常规构造和拷贝构造函数;编写析构函数。主程序中创建两个学生对象,一个常规构造,
一个拷贝构造;然后分别设置两个学生信息,再比较两个学生信息是否相同并输出结果。*/
#include<iostream>
using namespace std;
class student
{
private:char *name;long int num;int score;public:student(char *Name=NULL ,long int Num=0 ,int Score =0 ) //带默认形参的构造函数{name=Name;num=Num;score=Score;cout<<"构造成功!"<<endl;}~student() //析构函数{cout<<"析构成功!"<<endl;}student(student *p) //拷贝构造函数{name=p->name;num=p->num;score=p->score;cout<<"拷贝构造成功!"<<endl;}void write(char *Name,long int Num,int Score) //设置学生信息{name=Name;num=Num;score=Score;cout<<"设置学生信息成功!"<<endl;}void showinfo() //显示学生信息{cout<<name<<" "<<num<<" "<<score<<endl;}void compare(student a, student b) //比较学生信息是否相同 {if(a.num==b.num)cout<<"学号相同"<<endl;else cout<<"学号不相同"<<endl;if(a.score==b.score)cout<<"分数相同"<<endl;else cout<<"分数不相同"<<endl;if(strcmp(a.name,b.name)==0)cout<<"姓名相同"<<endl;else cout<<"姓名不相同"<<endl;}};
int main(){
student mate1("汪",01,100),mate2("徐",02,101);//构造两个student类对象
mate1.write("大佬A",01,100);
mate2.write("大佬B",02,100);
mate1.showinfo();
mate2.showinfo();
mate1.compare(mate1,mate2);
system("pause");
}
在student类中,使用了char *name来作为构造函数和write的形参,于是乎会发生E0167错误,按照其所说,我们将构造函数和write 的char *name 改为const char *name ,这时候会有第二个错误:
(2)E0513 不能将 “const char *” 类型的值分配到 “char *” 类型的实体
按照其所说,我们再将数据成员的char *name也改为const char *name,这时候可以运行
#include<iostream>
using namespace std;
class student
{
private:const char *name;long int num;int score;public:student(const char *Name=NULL ,long int Num=0 ,int Score =0 ) //带默认形参的构造函数{name=Name;num=Num;score=Score;cout<<"构造成功!"<<endl;}~student() //析构函数{cout<<"析构成功!"<<endl;}student(student *p) //拷贝构造函数{name=p->name;num=p->num;score=p->score;cout<<"拷贝构造成功!"<<endl;}void write(const char *Name,long int Num,int Score) //设置学生信息{name=Name;num=Num;score=Score;cout<<"设置学生信息成功!"<<endl;}void showinfo() //显示学生信息{cout<<name<<" "<<num<<" "<<score<<endl;}void compare(student a, student b) //比较学生信息是否相同 {if(a.num==b.num)cout<<"学号相同"<<endl;else cout<<"学号不相同"<<endl;if(a.score==b.score)cout<<"分数相同"<<endl;else cout<<"分数不相同"<<endl;if(strcmp(a.name,b.name)==0)cout<<"姓名相同"<<endl;else cout<<"姓名不相同"<<endl;}};
int main(){
student mate1("汪",01,100),mate2("徐",02,101);//构造两个student类对象
mate1.write("大佬A",01,100);
mate2.write("大佬B",02,100);
mate1.showinfo();
mate2.showinfo();
mate1.compare(mate1,mate2);
system("pause");
}
2.反思
作为入门时所写的题目,这么写算是完成任务了,但是const到底是怎么用的呢?
这一点在书P210,5.6.1函数实参的保护 ,有对const的说明:
函数的形参如果用const修饰,在函数体中该形参为只读变量,在函数体中不能对该形参变量进行修改。(就是意思说读进来一个常量)
如果形参变量类型是一个基本类型的变量,实参采用传值方式进行参数传递,无论在函数体中是否对形参变量进行了修改,都不会影响到实参。此时,const修饰形参的意义仅仅表明该形参是一个读入值,在函数体内没有对其进行修改。
但是,如果形参变量是指针型或者引用型,参数传递是传地址与“传名”方式,函数体对实参的修改会影响到实参,为了对实参进行保护,需要用const对实参进行修饰。C++中大量系统函数的形参均用const进行修饰。(这边说的也就是我们刚刚所干的事,函数形参用的指针型)
比如:
void insert(int i,ElemType e)中参数e是值传递,调用insert函数返回后实参的值不会改变。
而void insert(int i,ElemType &e)中e是传递的引用,如果insert函数中改变了e的值,相应的实参值也会改变。
另外,其实从上面应该也能看出来,函数中的不带修饰的参数默认是当做只读变量来处理的,也就是说其实只要数据成员形式也是const型就可以不用在函数形参那边加const,比如下面这段代码里面,student构造函数的形参就没用const修饰。
/*1.定义Date类,包含年月日三个数据成员,有设置日期和显示日期两个成员函数,有带默认值的常规构造和拷贝构造函数;√
2. 定义学生类,包含姓名,学号,绩点,出生日期(Date类对象)四个数据成员和一个静态数据成员学生总数。√有设置学生信息和显示学生信息两个成员函数,√有无参数的常规构造和带参数的常规构造函数以及析构函数√
3.在主程序中创建大小为5的动态学生数组,分别输入学生信息;然后统计所有学生的平均绩点加以输出。*/
#include<iostream>
using namespace std;
class Date
{
private:int year, month, day;
public:Date(){year = 2000;month = 1;day = 1;}Date(int YEAR, int MONTH, int DAY) //带默认值的常规构造函数{year = YEAR;month = MONTH;day = DAY;}Date(Date& p) //拷贝构造函数{year = p.year;month = p.month;day = p.day;}void setdate(int a, int b, int c) //设置日期{year = a;month = b;day = c;}void showdate() //显示日期{cout << year << "年" << month << "月" << day << "日" << endl;}
};class student
{
private:const char* name;long int num; //学号double score; //绩点Date birthtime; //Date类对象,出生日期static double total; //静态数据成员,内类声明
public:student() //不带参数的构造函数{name = "未知姓名";num = 0;score = 0;birthtime.setdate(0, 0, 0);cout << "(不带参数)构造成功" << endl;}student(char* Name, long int Num, double Score, int YEAR, int MONTH, int DAY) //带默认形参的构造函数{name = Name;num = Num;score = Score;birthtime.setdate(YEAR, MONTH, DAY);cout << "构造成功!" << endl;}~student() //析构函数{cout << "析构成功!" << endl;}student(student* p) //拷贝构造函数{name = p->name;num = p->num;score = p->score;birthtime = p->birthtime;cout << "拷贝构造成功!" << endl;}void write(const char* Name, long int Num, double Score, int a, int b, int c) //设置学生信息{name = Name;num = Num;score = Score;birthtime.setdate(a, b, c);cout << "设置学生信息成功!" << endl;total += Score;}void showinfo() //显示学生信息{cout << name << " " << num << " " << score << endl;birthtime.showdate();}static double ave_score(){cout << 0.2*total << endl;return total;}
};
double student::total = 0; //静态数据成员的类外定义
int main(){student* group; //建立一个对象数组指针group = new student[5]; //建立了一个指定对象为student的动态数组group[0].write("汪", 01, 4.0, 2001, 2, 2);group[1].write("徐", 02, 4.1, 2001, 3, 3);group[2].write("大佬A", 03, 4.2, 2001, 4, 4);group[3].write("大佬B", 01, 4.3, 2001, 5, 5);group[4].write("大佬C", 01, 4.4, 2001, 6, 6);group[4].ave_score(); //通过类名调用静态成员函数delete[]group; //用new申请的动态数组在此删除system("pause");
}
-----------------------✂--------------------------- 分割线-----------------------✂---------------------------
Ⅱ.字符数组
顾名思义就是用字符数组和strcpy来写,比如下面这段代码
/*1) 某出版系统发行图书和磁带,利用继承设计管理出版物的类。要求如下:建立一个基类Publication
存储出版物的标题title、出版物名称name、单价price及出版日期date。用Book类和Tape类分别管理图书和磁带,
它们都从Publication类派生。
Book类具有保存图书页数的数据成员page,Tape类具有保存播放时间的数据成员playtime。每个类都有构造函数、
析构函数,且都有用于从键盘获取数据的成员函数inputData(),用于显示数据的成员函数display()。
第一题用main中分别创建Book类和Tape类的对象数组(大小为3),输入并显示相关信息*/
#include<iostream>
using namespace std;
class Publication
{
private:char title[100];char name[100];double price;char date[100];
public:Publication(){strcpy_s(title,100, "未知标题");strcpy_s(name,100, "未知名称");strcpy_s(date,100, "未知出版日期");price = 0;}~Publication(){}void inputData(char Title[100],char Name[100],char Date[100],double Price){cout << "Please input the title:";cin >> Title;strcpy_s(title, 100, Title);cout << "Please input the name:";cin >> Name;strcpy_s(name, 100, Name);cout << "Please input the date:";cin >> Date;strcpy_s(date, 100, Date);cout << "Please input the price:";cin >> Price;price = Price;}void display(){cout << "The title is"<<title <<endl;cout << "The name is" << name << endl;cout << "The date is" <<date << endl;cout << "The price is" << price<< endl;}
};
class Book :public Publication
{
private:int page;
public:Book():Publication(){page = 0;cout << "基类构造成功" << endl;}~Book(){}void inputData(char Title[100], char Name[100], char Date[100], double Price, int Page){Publication::inputData(Title, Name,Date,Price);cout << "Please input page:";cin >> Page;page = Page;}void display(){Publication::display();cout <<"The page is"<< page << endl;}
};
class Tape :public Publication
{
private:long playtime;
public:Tape(){playtime = 0;}~Tape(){}void inputData(char Title[100], char Name[100], char Date[100], double Price, long Playtime){Publication::inputData(Title, Name,Date,Price);cout << "Please input playtime:";cin >> Playtime;playtime = Playtime;}void display(){Publication::display();cout << "The playtime is:"<<playtime << endl;}
};int main()
{char T[100], N[100], D[100];double price{0};int page{0};long playtime{0};Publication pub;Book novel[3];for (int i = 0; i < 3; i++)novel[i].inputData(T, N,D,price,page);for (int k = 0; k < 3; k++)novel[k].display();Tape eng[3]; for (int j = 0; j < 3; j++)eng[j].inputData(T, N, D, price,playtime);for (int m = 0; m < 3; m++)eng[m].display();system("pause");
}
基本操作就是创个字符数组,比如char a[100],然后输入的数据放在另一个数组char b[100],
strcpy_s(a, 100, b);另外这个b应该也能用字符指针之类的代替,不过我没试过,先鸽了,以后有时间再试吧
-----------------------✂--------------------------- 分割线-----------------------✂---------------------------
Ⅲ.string字符串
记得加#include<cstring>
还是来看个例题:
编写程序对大学里的人员进行管理。大学里的人员主要由学生,教师(教课),教员(不教课)和在职进修教师(既当学生又当教师)组成,各类人员均有姓名,电话和地址等信息,学生还有专业信息,员工另有所在部门及工资信息,教师另有教授课程信息,在职进修教师具备以上各类人员的信息。系统的类层次结构图如下:
#include<iostream>
#include<cstring>
using namespace std;
class Person {
private:string name,address;long long number;
public:Person(string name=" ",long long number =0,string address=" ") {this->name = name;this->number = number;this->address = address;}void Setperson(string n,long long num,string a){name = n;number = num;address = a;}void Showperson() {cout << "姓名:" << name << " 电话:" << number << " 地址:" << address;}~Person() {cout << "Person's desconstructor called.";}
};class Student :virtual public Person
{
private:string specialty;
public:Student(string name = " ", long long number = 0, string address = " ", string specialty = " ") :Person(name, number, address){this->specialty = specialty;}void SetStudent(string n, long long num, string a, string spe) {Setperson(n, num, a);specialty = spe;}void ShowStudent() {cout << "学生. ";Showperson();cout << " 专业:" << specialty;}~Student() {cout << "Student's desconstructor called.";}
};class Staff :virtual public Person
{
private:string department;double salary;
public:Staff(string name = " ", long long number = 0, string address = " ",string department=" ",double salary=0) :Person(name, number, address){this->department = department;this->salary = salary;}void SetStaff(string n, long long num, string a, string d,double sal) {Setperson(n, num, a);department = d;salary = sal;}void ShowStaff() {cout << "教员. ";Showperson();cout << " 所在部门:" << department<<" 工资:"<<salary<<"元";}~Staff() {cout << "Staff's desconstructor called.";}
};class Teacher :public Staff
{
private:string course;
public:Teacher(string name = " ", long long number = 0, string address = " ", string department = " ", double salary = 0, string course = " ") :Staff(name, number, address, department,salary){this->course = course;}void SetTeacher(string n, long long num, string a, string d, double sal,string cou) {SetStaff(n,num,a,d,sal);course = cou;}void ShowTeacher() {cout << "教师. ";ShowStaff();cout << " 教授课程:" << course;}~Teacher() {cout << "Teacher's desconstructor called.";}
};class StudentTeacher :public Student, public Teacher
{
public:StudentTeacher(string name = " ", long long number = 0, string address = " ", string specialty = " ", string department = " ", double salary = 0, string course = " ") :Student(name, number, address, specialty),Teacher(name,number,address,department,salary,course){}void SetStudentTeacher(string n, long long num, string a, string spe, string d, double sal, string cou) {SetStudent(n,num,a,spe);SetTeacher(n,num,a,d,sal,cou);}void ShowStudentTeacher() {cout << "员工进修教师. ";ShowStudent();ShowTeacher();}
};
int main() {string n, a, spe, d, cou;long long num;double sal;char c = '*';Student p1;Staff p2;Teacher p3;StudentTeacher p4;cout << endl << "请输入学生的姓名、电话、地址、专业信息:";cin >> n >> num >> a >> spe;p1.SetStudent(n, num, a, spe);p1.ShowStudent();cout << endl;cout << endl << "请输入教员的姓名、电话、地址、所在部门、工资:";cin >> n >> num >> a >> d >> sal;p2.SetStaff(n, num, a, d, sal);p2.ShowStaff();cout << endl;cout << endl << "请输入教师的姓名、电话、地址、所在部门、工资、教授课程:";cin >> n >> num >> a >> d >> sal >> cou;p3.SetTeacher(n, num, a, d, sal, cou);p3.ShowTeacher();cout << endl;cout << endl << "请输入员工进修教师的姓名、电话、地址、专业信息、所在部门、工资、教授课程:";cin >> n >> num >> a >> spe >> d >> sal >> cou;p4.SetStudentTeacher(n, num, a, spe, d, sal, cou);p4.ShowStudentTeacher();cout << endl;system("PAUSE");
}
很明显string类比上面几个好用多了,看着也清楚多了。
(顺便一说,上面那题,如果作为间接派生类,需要给虚基类Person提供初始化参数,10号上机强调的重点)