题目整理自网络,版权归原作者所有。
1.程序改错(有一处错误)
#include <iostream>
using namespace std;
class Square
{
private:
int n;
public:
Square();
Square(int nn);
int square();
};
Square::Square(){
n=0;
}
Square::Square(int nn){
n=nn;
}
Square::double square(){
return double(n*n);
}
int main()
{
Square s(10);
cout<<s.square()<<endl;
return 0;
}
错误在于 Square::double square() 函数的返回类型和声明不一致。由于只让改一处,将11行int square();改为double square();
2.程序改错(有一处错误)
#include <iostream>
using namespace std;
class A
{
protected:
int a1,a2;
public:
A(){
a1=0;a2=0;
}
A(int aa1,int aa2)
{
a1=aa1;a2=aa2;
}
int add()
{
return a1+a2;
}
};
int main()
{
A a;
cout<<"input first number:";cin>>a.a1;
cout<<"input second number:";cin>>a.a2;
cout<<"sum="<<a.add()<<endl;
return 0;
}
问题在于尝试在 main
函数中直接访问 A
类的保护成员 a1
和 a2
。在 C++ 中,保护成员只能在类的成员函数、友元函数或派生类中访问,而不能在类的外部直接访问。故将类A中的protected改为public。
3.程序改错(有一处错误)
#include <iostream>
#include<string>
#include<ctime>
#include<cstdlib>
using namespace std;
class A
{
protected:
int x;
public:
A(){
x=0;
}
A(int xx)
{
x=xx;
}
void display()
{
cout<<x<<endl;
}
};
class B:A{
protected:
int y;
public:
B(){
y=0;
}
B(int xx,int yy):
{
y=yy;
}
void display()
{
cout<<x<<endl;
cout<<y<<endl;
}
};
int main()
{
B b(1,2);
b.display();
return 0;
}
class B
继承自 class A
,但是在 B
的构造函数定义中存在错误,没有正确地初始化基类 A
的成员 x
。在 C++ 中,当一个派生类构造函数被调用时,它需要负责初始化自己的成员以及基类的成员。为了初始化基类的成员,派生类构造函数使用初始化列表来调用基类的构造函数。这是因为在派生类构造函数开始执行之前,基类的构造函数必须先被调用。
故将32行修改为B(int xx,int yy):A(xx)
4.程序填空
//重载“-”运算符,从对象的数据(s串)中删除所有指定的子串(s1串)
//重载“<<”运算符,输出对象中的数据(s串)
#include <iostream>
#include<string>
#include<ctime>
#include<cstdlib>
using namespace std;
class A
{
private:
string s;
public:
A(string ss=""){
s=ss;
}
void input(){
cout<<"input string:";
cin>>s;
}
void output(){
cout<<"the string is:"<<s<<endl;
}
A operator-(A a){
A temp;
string s1=a.s;
string::size_type n=0;
while(true)
{
if(s.find(s1,n)!=string::npos)
{
//------------------------------------------
//填空
//------------------------------------------
s.erase(n,s1.size())
}
else
break;
}
temp.s=s;
return temp;
}
};
ostream& operator<<(ostream& os,A a)
{
//-------------------------------------
//填空
//-------------------------------------
return os;
}
int main()
{
A s1("this is a book!");
A s2("is");
cout<<s1;
cout<<s2;
cout<<s1-s2;
return 0;
}
首先明确几个相关概念
find
方法:find
方法是std::string
类的一个成员函数,用于在字符串中查找子串。它接受两个参数:要查找的子串和开始查找的位置。如果找到了子串,它返回子串在字符串中第一次出现的位置;如果没有找到,它返回std::string::npos
。erase
方法:erase
方法也是std::string
类的一个成员函数,用于从字符串中删除字符。它接受两个参数:开始删除的位置和要删除的字符数。这个方法会从指定的位置开始删除指定数量的字符。string::size_type n = 0;
: 这行代码声明了一个变量n
,其类型为string::size_type
。string::size_type
是一个无符号整数类型,通常用于表示字符串的大小或索引。在这里,n
被初始化为 0,表示从字符串的开始位置开始查找子串。ostream& operator<<(ostream& os, A a)
: 这是一个重载的输出运算符函数。它接受一个ostream
引用(如cout
)和一个A
类的对象a
。这个函数的作用是将A
对象中的字符串输出到ostream
对象os
中。函数的返回类型是ostream&
,这意味着它返回对os
的引用,允许链式输出操作,例如cout << a << b;
故第一个空处填n=s.find(s1,n);第二个空处填a.output();
5.编程综合题 从键盘输入一个整数,求它的平方。要求做容错处理,判断输入的是否为数字。
//one possible version
#include <iostream>
#include<string>
#include<sstream>
using namespace std;
class Square
{
private:
int n;
string s;
public:
Square(){//构造函数
}
int str2num(string s){//串转数
int temp;
stringstream ss;
//stringstream 是 C++ 标准库中的一个类,它允许你在字符串和基本数据类型之间进行转换。
//通过创建一个 stringstream 对象,你可以将字符串插入到流中,或者从流中提取数据并转换为其他数据类型。
//在这里,ss 是一个 stringstream 对象,用于字符串和整数之间的转换。
ss<<s;
//这行代码使用插入运算符 << 将字符串 s 插入到 stringstream 对象 ss 中。
//这相当于将字符串 s 的内容“放入”流 ss 中,以便后续可以从这个流中提取数据。
ss>>temp;
//这行代码使用提取运算符 >> 从 stringstream 对象 ss 中提取数据,并将其存储在整数变量 temp 中。
//这个操作将流中的字符串转换为整数。
//如果流中的字符串格式正确(即它表示一个有效的整数),temp 将包含转换后的整数值。
return temp;
}
bool input(){//输入并判断是否是数字
bool flag=true;
cout<<"input a number:";
cin>>s;
for(int i=0;i<s.size(),++i){
//s.size()是 std::string 类的一个成员函数,用于返回字符串 s 的长度(即字符串中字符的数量)。
//在这个上下文中,s.size() 返回字符串 s 中的字符数,用于控制循环或确定字符串的长度。
if(s[i]>='0'&&s[i]<='9')
continue;
else{
cout<<"输入的不是数字,请重新输入!"<<endl;
flag=false;
break;
}
}
return flag;
}
void cal()
{//计算平方
while(true)
{
if(input()==true)
{
n=str2num(s);
cout<<"square:"<<n*n<<endl;
break;
}
}
}
};
int main()
{
Square s;
s.cal();
return 0;
}
6.程序填空
现有程序预期的输入输出如下:
输入:abc def
输出:
abc,abc,abc
def,def,def
#include <iostream>
using namespace std;
#include <string>
#include <cstring>
class StringPlus
{
char *p;
public:
StringPlus(const char *s)
{
if (s)
{
p = new char[strlen(s) + 1]
strcpy(p, s);
}
else
p = NULL;
}
StringPlus(const StringPlus &s)
{
if (s.p != NULL)
{
p = new char[strlen(s.p) + 1];
strcpy(p, s.p);
}
else
p = NULL;
}
~StringPlus()
{
if (p)
delete[] p;
}
//类的定义不完整,请补充
};
int main()
{
char w1[200] , w2[100];
while (cin>>w1>>w2)
{
StringPlus s1(w1), s2 = s1;
StringPlus s3(NULL);
s3.Copy(w1);
cout << s1 << "," << s2 << "," << s3 << endl;
s2 = w2;
s3 = s2;
s1 = s3;
cout << s1 << "," << s2 << "," << s3 << endl;
}
}
答案如下
void Copy(char *s)
{
//这个函数用于将一个字符串 s 复制到 StringPlus 对象的内部字符数组 p 中。
//如果 s 不是 NULL,它会先删除原有的 p(如果有的话),然后分配新的内存并复制 s 的内容。
//如果 s 是 NULL,并且 p 不是 NULL,它会删除 p 并将其设置为 NULL。
if (s != NULL)
{
if (p != NULL)
{
delete[] p;
}
p = new char[strlen(s) + 1];
strcpy(p, s);
}
else if (p != NULL)
{
delete[] p;
p = NULL;
}
}
StringPlus &operator=(const StringPlus &s)
{
//这个函数重载了赋值运算符,使得 StringPlus 对象可以被赋值。
//如果源对象 s 的 p 不是 NULL,它会先删除当前对象的 p(如果有的话),然后分配新的内存并复制 s.p 的内容。
//如果 s.p 是 NULL,并且当前对象的 p 不是 NULL,它会删除 p 并将其设置为 NULL。
///函数返回当前对象的引用,以便支持链式赋值。
if (s.p != NULL)
{
if (p != NULL)
delete[] p;
p = new char[strlen(s.p) + 1];
strcpy(p, s.p);
}
else if (p != NULL)
{
delete[] p;
p = NULL;
}
return *this;
}
friend ostream &operator<<(ostream &out, StringPlus &s)
{
//这个函数重载了输出运算符,使得 StringPlus 对象可以直接通过 cout 输出。
//它接受一个 ostream 引用和一个 StringPlus 引用,将 StringPlus 对象的内部字符数组 p 的内容输出到流 out 中,
//并返回该流,以便支持链式输出。
out << s.p;
//这是输出运算符重载的一部分,用于将 StringPlus 对象的内容输出到输出流 out 中。
//在这个上下文中,out 是一个 ostream 对象(例如 cout),s 是一个 StringPlus 对象。
//s.p 是 StringPlus 对象内部的字符数组,存储了实际的字符串内容。
//通过 out << s.p;,字符串内容被插入到输出流中,从而可以被打印到控制台或其他输出设备。
//这个操作是输出运算符重载的核心,它允许用户直接使用 cout 来输出 StringPlus 对象,就像输出普通字符串一样。
return out;
}
/*整个程序的逻辑是:
在 main 函数中,创建了两个字符数组 w1 和 w2。
进入一个无限循环,创建 StringPlus 对象 s1 和 s2,并复制 s1 到 s2。
创建 s3 并使用 Copy 函数从 w1 复制内容到 s3。
输出 s1、s2 和 s3 的内容。
然后进行一系列的赋值操作,改变 s1、s2 和 s3 的内容,并再次输出。
最后通过 break 退出循环。*/
7.编程综合题 允许输入三次密码,输入密码时显示为“*”。(提示:用getch()函数无回显输入单个字符)
//one possible version
#include <iostream>
#include<string>
#include<conio.h>
using namespace std;
class Password
{
private:
int times;
string password;
public:
Password(string ss,int nn)
{
password=ss;
times=nn;
}
string Input1()
{
string temp;
char c;
cout<<"input password:";
while(true)
{
c=getch();
if(c!=13)
{//在 ASCII 码中,13 对应的是回车键(Enter 键)。
//这个条件判断的目的是检查用户是否按下了回车键。如果不是回车键,则执行花括号内的代码块。
temp=temp+c;
cout<<'*';
}
else break;
}
cout<<endl;
return temp;
}
void input2()
{
string temp;
while(true)
{
temp=input1();
if(temp==password){
cout<<"密码正确!"<<endl;
break;
}
else{
cout<<"密码不正确!"<<endl;
--times;
if(times<=0){
cout<<"输入错误,系统已退出!";
break;
}
}
}
}
};
int main()
{
Password p("123456",3);
p.input2();
return 0;
}
8.编写程序,从文件text.dat中读取数据并打印。已有类申明及部分主函数如下:
#include <iostream>
#include<fstream>
using namespace std;
#define MAX 10
class Student
{
public:
char name[25];
int Ex1,Ex2;
};
int main()
{
Student st[MAX];
int count = 0;
ifstream in("test.dat",ios::in);
if(!in)
{
cout<<"Cannot open test.dat.\n";
return 1;
}
in>>st[count].name>>st[count].Ex1>>st[count].Ex2;
//请补充程序---------//
//-------------------//
in.close();
return 0;
}
答案如下
//one possible version
while(!in.eof())
{
cout<<st[count].name<<""<<st[count].Ex1<<""<<st[count].Ex2<<"\n";
count++;
if(cout>=10)
{
cout<<"Exceed MAX:"<<MAX<<endl;
in.close();
return (-1);
}
in>>st[count].name>>st[count].Ex1>>st[count].Ex2;
}
9.写出下列代码的运行结果,并简述运行过程
#include <iostream>
#include<string>
using namespace std;
class Oyster{
public:
Oyster(string genusString)
{
genus=genusString;
}
string getPhylum() const{//phylum 门类
return "Mollusca";
}
virtual string getName() const{
return "Oyster class";
}
string getGenus() const{//genus 属类
return genus;
}
virtual void print() const=0;
private:
string genus;
};
class VirginiaOyster:public Oyster{
public:
VirginiaOyster():Oyster("Crassostrea"){//太平洋
}
virtual string getName() const{
return"VirginiaOyster class";
}
virtual void print() const{
cout<<"Phylum:"<<getPhylum().c_str()<<"\tGenus:"<<getGenus().c_str()<<"\tName:"<<getName().c_str();
}
//.c_str()是一个字符串处理函数,它用于将C++中的std::string类型转换为C语言风格的字符串,即以null结尾的字符数组
};
int main()
{
VirginiaOyster oyster;
Oyster *baseClassPtr;
baseClassPtr=&oyster;
baseClassPtr->print();
cout<<endl;
return 0;
}
运行过程如下:
- 创建了一个
VirginiaOyster
对象oyster
。 - 定义了一个指向
Oyster
类的指针baseClassPtr
,并将其指向oyster
对象。 - 调用
baseClassPtr->print()
。 - 在
VirginiaOyster
类中,实现了print()
函数,先调用getPhylum()
函数返回字符串"Mollusca",再调用getGenus()
函数返回字符串"Crassostrea",最后调用getName()
函数返回字符串"VirginiaOyster class",将这些信息打印出来。 - 输出结果为"Phylum: Mollusca Genus: Crassostrea Name: VirginiaOyster class"。
10.知识点整理——try和throw关键字
当我们编写代码时,可能会遇到潜在的错误或异常情况。为了处理这些异常,我们可以使用try
和throw
关键字。
try
语句块用于包围可能会抛出异常的代码块,它的作用是捕获并处理异常。try
语句块的基本语法如下:
try {
// 可能会抛出异常的代码块
} catch (ExceptionType1 e1) {
// 捕获并处理异常类型1的代码块
} catch (ExceptionType2 e2) {
// 捕获并处理异常类型2的代码块
} catch (...) {
// 捕获并处理其他异常的代码块
}
在try
语句块中的代码可能会抛出异常,当抛出异常时,程序会立即跳转到匹配的catch
语句块中。
而throw
关键字用于在代码中主动抛出异常。我们可以使用throw
关键字抛出一个异常对象,它可以是任意类型的对象,通常是派生自std::exception
类或其子类的对象。throw
的基本用法如下:
throw ExceptionType(); // 抛出一个异常对象
当throw
语句执行时,程序会立即跳转到调用栈中的最近的异常处理语句,寻找匹配的catch
语句块来处理异常。
结合起来,try
和throw
关键字一起使用可以实现异常处理机制。我们使用try
语句块包围可能会抛出异常的代码块,当在try
语句块中的代码抛出异常时,程序会跳转到匹配的catch
语句块中,用于捕获并处理异常。
11.问答题 已知Base类和Derive类有如下继承关系:
class Base{
public:
Base();
virtual ~BaseV()
};
class Derived:public Base{
public:
Derived();
~Derived();
void PrintMessage();
};
请问:下面的函数是否有Bug,如果有,请说明并修正。
void foo(Base* b){
Derived*pd=dynamic_cast<Derived*>(b);
d->PrintMessage();
}
dynamic_cast
是C++中的一个运算符,用于在类层次结构中进行安全的向下类型转换(downcast)。
在多态的情况下,当基类指针或引用指向派生类对象时,可以使用dynamic_cast
将其转换为派生类指针或引用。这个转换是在运行时进行的,因此可以动态地检查是否可以进行安全的类型转换。
在使用dynamic_cast
时,需要谨慎处理转换失败的情况,以避免访问无效的指针或引用。通常,可以通过检查转换结果是否为空指针来判断转换是否成功,并采取相应的错误处理措施。
在函数foo
中,使用了动态类型转换dynamic_cast
将Base*
指针转换为Derived*
指针,并尝试调用PrintMessage
函数。
然而,如果传递给foo
函数的Base*
指针指向的对象不是Derived
类的实例,那么dynamic_cast
会返回一个空指针,而空指针是无效的,尝试通过空指针调用成员函数PrintMessage
会导致未定义行为。
为了修复这个问题,我们应该在调用PrintMessage
函数之前,先检查dynamic_cast
的返回值是否为空指针。如果为空指针,我们可以选择相应的错误处理方法,例如抛出异常、输出错误消息等。
修正为在两行间插入if(d);
12.问答题 运行下面的程序会产生何种结果,为什么?
#include <iostream>
#include<string>
#include<cstdlib>
#include<cstdio>
using namespace std;
int main()
{
char *str=(char*)malloc(100);
strcpy(str,"hello");
free(str);
if(str!=NULL)
{
strcpy(str,"world");
printf(str);
}
return 0;
}
free(str);
释放了分配给 str
的内存。在 free(str)
之后,str
指向的内存区域不再有效,因此后续对 str
的任何操作(如 strcpy
和 printf
)都会导致未定义行为。如果在 free(str)
之后不再需要 str
,那么释放内存是正确的。但是,如果在释放内存后仍然尝试使用 str
,这会导致内存泄漏,因为 str
指向的内存区域已经被释放,无法再次安全地使用。篡改动态内存区的内容,后果难以预料,非常危险。
13.编写求数组最小值的模板函数,使得如下程序输出:10,1
int main()
{
int arr1[]={10,20,15,12};
int n1=sizeof(arr1)/sizeof(arr1[0]);
int arr2[]={1,2,3};
int n2=sizeof(arr2)/sizeof(arr2[0]);
cout<<arrMin<int>(arr1,n1)<<endl;
cout<<arrMin<char>(arr2,n1)<<endl;
return 0;
}
答案如下
template <class T>
int arrMin(T arr[],int len)
{
int min=arr[0];
for(int i=0;i<len;i++){
if(arr[i]<=min)
{
min=arr[i];
}
}
return min;
}//这里存在一些问题。函数的返回类型与函数体内使用的类型不一致。
//函数 arrMin 被声明为返回 int,但在函数体内,min 变量是模板类型 T。
//这意味着如果 T 不是 int,那么返回的值将与函数的返回类型不匹配,导致编译错误。
template <class T>
T arrMin(T arr[], int len)
{
T min = arr[0];
for (int i = 0; i < len; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
14.编程题 运行如下文件后,文件:example.txt中的内容是什么?
编写程序读取example.txt中的内容并显示
#include <iostream>
#include<fstream>
using namespace std;
int main()
{
ofstream myfile("example.txt");
if(myfile.is_open())
{
myfile<<"This is a line.\n";
myfile<<"this is another line\n";
myfile.close();
}
else cout<<"unable to open file";
return 0;
}
答案如下
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream myfile("example.txt");
if (myfile.is_open())
{
string line;
while (!myfile.eof())
{
getline(myfile,line);
//是 C++ 标准库中的一个函数调用,用于从输入流 myfile 中读取一行文本,
//并将读取的内容存储在字符串变量 line 中。这个函数通常用于处理文件或标准输入(如键盘输入)。
cout << line << endl;
}
myfile.close();
}
else
{
cout << "Unable to open file";
}
return 0;
}
15.定义一个名为“computer”的类,该类包含一个名为”name“的数据成员和一个名为”print“的成员函数,”print“用来打印该”name“数据成员;定义一个从”computer“派生而来的名为”Macintosh“的类,该类包含一个名为”color“的数据成员和一个名为”print“的成员函数,该成员函数用来打印”name“和”color“。
下面是用来测试的main函数
int main()
{
computer *p;
Macintosh imac("Joe`s IMAC","Blue");
p=&imac;
p->print();
return 0;
}
希望上面的测试程序得到如下的输出结果
name:Joe`s IMAC
color:Blue
答案如下
//one possible version
class computer
{
public:
string _name;
computer(string name){
_name= name;
}
virtual void print(){
cout<<"name:"<<_name<<endl;
}
};
class Macintosh : public computer{
public:
string _color;
Macintosh(string name,string color):computer(name)
{
_color=color;
}
void print(){
computer::print();
cout<<"color:"<<_color<<endl;
}
};
16.已知类string的原型为:
class String{
public:
String(const char *str=NULL);
String(const String &other);
~String(void);
String & operate=(const String &other);
private:
char *m_data;
};
请编写String的拷贝构造函数和赋值函数。
//析构函数
String::~String(void){
delete[] m_data;
}
//普通构造函数
String::String(const char *str)
{
if(str==NULL)
{
m_data=new char[1];
*m_data="\0";
}
else{
int length=strlen(str);
m_data=new char[length+1];
strcpy(m_data,str);
}
}
//拷贝构造函数
String::String(const String &other)
{
int length=strlen(other.m_data);
m_data=new char[length+1];
strcpy(m_data,other.m_data);
}
//赋值函数
String &String::operate=(const String &other)
{
//检查自赋值
if(this==&other)
return *this;
//释放原有的内存资源
delete[] m_data;
//分配新的内存资源,并复制内容
int length=strlen(other.m_data);
m_data=new char[length+1];
strcpy(m_data,other.m_data);
//返回本对象的引用
return *this;
}