第6章 数组、指针与字符串
6-20 实现一个名为 SimpleCircle 的简单圆类。其数据成员 int* itsRadius 为一个指向其半径值的指针,存放其半径值。设计对数据成员的各种操作,给出这个类的完整实现并测试这个类。
#include<iostream>
using namespace std;
const float PI=3.14;
class SimpleCircle{
private:
int* itsRadius;
public:
SimpleCircle(){
itsRadius = new int(5);
}
SimpleCircle(int R){
itsRadius = new int(R);
}
SimpleCircle(const SimpleCircle &rhs){
int val = rhs.getRadius();
itsRadius = new int(val);
}
~SimpleCircle() {}
int getRadius() const{
return *itsRadius;
}
float area(){
return PI*(*itsRadius)*(*itsRadius);
}
};
int main()
{
int R;
cout<<"请输入s2圆的半径:";
cin>>R;
SimpleCircle s1,s2(R),s3(s2);
cout<<"该圆s1的面积是:"<<s1.area()<<endl;
cout<<"该圆s2的面积是:"<<s2.area()<<endl;
cout<<"该圆s3的面积是:"<<s3.area()<<endl;
return 0;
}
6-21 编写一个函数,统计一条英文句子中字母的个数,在主程序中实现输入输出。
#include<iostream>
#include<string>
using namespace std;
int count(char *s)
{
int n;
for(int i=0;s[i]!='\0';i++){
if((s[i]>='A'&&s[i]<='Z')||(s[i]>='a'&&s[i]<='z'))
n++;
}
return n;
}
int main()
{
char s[100];
cout<<"请输入一个英文句子:";
gets(s); //输入可以带空格的字符串
cout<<"这个句子中字母有"<<count(s)<<"个"<<endl;
return 0;
}
6-22 编写函数 void reverse(string & s),用递归算法使字符串 s倒序。
#include <iostream>
#include <string>
using namespace std;
string reverse(string& str)
{
if(str.length()>1){
string sub = str.substr(1,str.length()-2);
return str.substr(str.length()-1,1) + reverse(sub) + str.substr(0,1);
}
else
return str;
}
int main()
{
string str;
cout<<"请输入一个字符串:";
cin>>str;
cout<<"倒序反转后的字符串为:"<<reverse(str)<<endl;
return 0;
}
6-23 设学生人数 N = 8,提示用户输入 N 个人的考试成绩,然后计算出他们的平均成绩并显示出来。
#include <iostream>
using namespace std;
int main()
{
float num[8],score = 0,ave;
cout<<"请输入8个人的考试成绩:"<<endl;
for(int i=0;i<8;i++){
cin>>num[i];
score+=num[i];
}
ave=score/8;
cout<<"它们的平均成绩为:"<<ave<<endl;
return 0;
}
6-24 基于 char*设计一个字符串类 M yString,并且具有构造函数、析构函数、复制构造函数,重载运算符“+”,“=”,“+=”,“[]”,尽可能完善它,使之能满足各种需要(运算符重载功能为选做, 参见第 8 章)。
#include <iostream>
#include <cstring>
using namespace std;
class myString{
private:
char *s;
int Size;
public:
myString(){ //无参数构造函数
s=new char[1];
s[0]='\0';
Size=0;
}
myString(char str[]){ //有参数构造函数
Size=strlen(str);
s=new char[Size+1];
strcpy(s,str);
}
myString( const myString &b ){ //复制构造函数
Size=strlen(b.s);
s=new char[Size+1];
strcpy(s,b.s);
}
~myString(){ //析构函数
delete []s;
Size=0;
}
int size(){ //返回字符串长度
return Size;
}
int set(char str[]){ //拷贝字符串str
Size=strlen(str);
s=new char[Size+1];
strcpy(s,str);
return Size;
}
myString& operator= ( const myString &b ){ //重载运算符"="
Size=b.Size;
s=new char[Size+1];
strcpy(s,b.s);
return *this; //赋值运算,返回给自身this
}
myString operator+ ( const myString &b ){ //重载运算符"+"
myString m;
int l=Size+b.Size;
m.s=new char[l+1];
m.Size=l;
strcat(s,b.s);
strcpy(m.s,s);
return m; //返回一个新对象的字符串
}
myString& operator+= ( const myString &b ){ //重载运算符"+="
myString m;
int l=Size+b.Size;
m.s=new char[l+1];
m.Size=l;
strcat(s,b.s);
strcpy(m.s,s);
*this=m;
return *this; //相加完的字符串返回给自身this
}
char &operator[] (int l){ //重载运算符"[]"
return s[l]; //返回字符串
}
myString sub( int start, int l ){
//返回一个 myString 对象m,其数据 s 为当前对象的 s[start, start + l] 范围内的字符
myString m;
m.s=new char[l+1];
m.Size=l;
for(int i=0;i<l;i++)
m.s[i]='\0';
int j=0;
for(int i=start-1;(i<start+l-1)&&(i<Size);i++,j++){
m.s[j]=s[i];
}
return m;
}
void output(){ //输出字符串
cout<<s<<endl;
}
};
int main()
{
myString tmp;
cout << tmp.size() << endl; // 预期输出:0
char t[20]; //重载"[]"
strcpy(t,"Hello");
tmp += t;
cout << t << endl; // 预期输出:Hello
char s1[] = "abcd";
myString a( s1 );
a.output(); // 预期输出:abcd
cout << a.size() << endl; // 预期输出:4
char s2[] = "1234";
myString b( s2 );
b.output(); // 预期输出:1234
myString c;
c = a; // 运算符"="重载
c.output(); // 预期输出:abcd
cout << c.size() << endl; // 预期输出:4
c = a + b; //运算符“+ ”重载
c.output(); // 预期输出:abcd1234
cout << c.size() << endl; // 预期输出:8
myString d(c); //复制构造函数
d.output(); // 预期输出:abcd1234
d += c; //运算符“+= ”重载
d.output(); //预期输出:abcd1234abcd1234
b = c.sub( 3, 4 );
b.output(); // 预期输出:cd12
b = c.sub( 5, 10 ); // 从第5个位置至末尾,最多只有4个字符,故取完这4个字符即结束
b.output(); // 预期输出:1234
char s3[] = "6666666666";
c.set( s3 );
c.output(); // 预期输出10个6
return 0;
}
6-25 编写一个 3×3 矩阵转置的函数,在 main()函数中输入数据。
#include <iostream>
#include <string>
using namespace std;
int a[3][3];
void zhuanzhi(int a[3][3])
{
for(int i=0;i<3;i++)
for(int j=0;j<i;j++){
int k=a[i][j];
a[i][j]=a[j][i];
a[j][i]=k;
}
}
int main()
{
cout<<"请输入一个3*3的矩阵:"<<endl;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
cin>>a[i][j];
zhuanzhi(a);
cout<<"转置后矩阵为:"<<endl;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
return 0;
}
6-26 编写一个矩阵转置的函数,矩阵的行数和列数在程序中由用户输入。
#include<iostream>
using namespace std;
void exchange(int *p1,int *p2,int r,int c)
{
int i,j;
cout<<"输入的矩阵为:"<<endl;
for(i=0;i<r;i++){
for(j=0;j<c;j++)
cin>>p1[i*c+j];
}
for(i=0;i<c;i++)
for(j=0;j<r;j++)
p2[i*r+j]=p1[i+c*j]; //矩阵转置;这里的i j分别代表转置后的行列
cout<<"转置后的矩阵为:"<<endl;
for(i=0;i<c;i++){ //输出矩阵
for(j=0;j<r;j++)
cout<<p2[i*r+j]<<' ';
cout<<endl;
}
}
int main(){
int r,c;
cout<<"请输入矩阵行数、列数:"<<endl;
cin>>r>>c;
int *p3=new int[r*c];
int *p4=new int[r*c];
exchange(p3,p4,r,c);
delete p3;
delete p4;
return 0;
}
6-27 定义一个 Employee 类,其中包括表示姓名、地址、城市和邮编等属性,包括setName()和display()等函数。display()使用cout语句显示姓名、地址、城市和邮编等属性,函数 setName()改变对象的姓名属性,实现并测试这个类。
#include <iostream>
#include <cstring>
using namespace std;
class Employee
{
private:
char name[50];
char street[60];
char city[60];
char zip[80];
public:
Employee(const char *n, const char *str, const char *ct, const char *z);
void change_name(const char *n);
void display();
};
Employee::Employee(const char *n, const char *str, const char *ct, const char *z)
{
strcpy(name,n);
strcpy(street,str);
strcpy(city,ct);
strcpy(zip,z);
}
void Employee::change_name(const char *n)
{
strcpy(name,n);
}
void Employee::display()
{
cout << name << " " << street << " "<< city << " " << zip;
}
int main()
{
Employee x("美国队长", "碧桂园2号楼", "地球", "34534534");
x.display();
cout << endl;
x.change_name("钢铁侠");
x.display();
cout << endl;
return 0;
}
其中,构造函数Employee和成员函数change_name()的形参都是使用指针来进行传递,类型const char *表示传入的形参为字符串常量,是无法改变的。对字符型的私有成员进行初始化赋值时,使用的是<cstring>头文件中的strcpy()函数。
此外,构造函数Employee的形参可以使用指针,也可以不使用。但使用指针可以提高程序的编译效率和执行速度,使程序更加简洁,也实现动态内存分配,减少内存的消耗。可以直接操纵内存地址,从而可以完成和汇编语言类似的工作。
6-28 分别将例6-10程序和例6-16程序中对指针的所有使用都改写为之等价的引用形式,比较修改前后的程序,体会在哪些情况下使用指针更好,哪些情况下使用引用更好。
//例6-10程序改写
#include<iostream>
using namespace std;
void splitFloat(float x,int &intPart,float &fracPart)
{
intPart = static_cast<int>(x);
fracPart = x-intPart;
}
int main()
{
cout<<"Enter 3 float point numbers:"<<endl;
for(int i=0;i<3;i++){
float x,f;
int n;
cin>>x;
splitFloat(x,n,f);
cout<<"Integer Part="<<n<<" Fraction Part="<<f<<endl;
}
return 0;
}
//例6-16程序改写
#include<iostream>
using namespace std;
class Point{
public:
Point():x(0),y(0){
cout<<"Default Constructor called."<<endl;
}
Point(int x,int y):x(x),y(y){
cout<<"Constructor called."<<endl;
}
~Point(){ cout<<"Destructor called."<<endl;}
int getX() const{ return x; }
int getY() const{ return y; }
void move(int newX,int newY){
x=newX;
y=newY;
}
private:
int x,y;
};
int main()
{
cout<<"Step one:"<<endl;
Point &ptr1 = *new Point;
delete &ptr1;
cout<<"Step two:"<<endl;
ptr1 = *new Point(1,2);
delete &ptr1;
return 0;
}
指针和引用的对比与分析:
- 引用是一个别名,不能为NULL值,初始化指定被引用的对象后不能被重新分配了。引用本身(而非被引用的对象)的地址是不可以获得的,是被隐藏的。引用一经定义初始化后,对于它的全部行为,全是针对被引用对象的;
- 指针是一个存放地址的变量。它可以被多次赋值,当需要的对变量重新赋以另外的地址或赋值为NULL时只能使用指针。
- 指针能进行动态内存分配和管理,减少内存的消耗。地址是变量的门牌号,指针传递大型数据的地址而不是传值,就像快捷方式一样可以省空间,省时间,提高了效率。
- 通过指针被调用函数可以向调用函数处返回除正常的返回值之外的其他数据,从而实现两者间的双向通信。
- 更容易实现函数的编写和调用。
- 如果想要创建一些表示和实现各种复杂、大型的数据结构或者动态分配空间时,那么使用指针可以编写出更加高质量的程序,更灵活地分配好空间,改善某些子程序的效率。
- 但对于简单小型的数据双向传递、减少大对象的参数传递开销这两个用途来说,引用可以很好地代替指针,它不需要像指针那样需要在主调函数中用“&”操作符传递函数,在被调函数中用“*”操作符使来访问数据,用引用比指针更加简洁、安全。
哦,对了,别忘记……
本专栏为本人大二C++课程的习题作业和一些学习经验的分享,供大家参考学习。如有侵权请立即与我联系,我将及时处理。
参考书籍为:C++语言程序设计 第五版 -清华大学出版社- 郑莉,董渊、C++语言程序设计 第五版 -清华大学出版社- 郑莉,董渊(学生用书)
编译环境:Visual Studio 2019、Dev-C++
欢迎关注我的微信公众号,分享一些有趣的知识:程序猿的茶水间