Bootstrap

C++ 复习(含PTA题目)

目录

判断题

选择题

填空题

程序填空题

构造函数与析构函数

判断题

选择题

填空题

程序填空题

函数题

this指针

选择题

对象数组和对象指针

判断题

选择题

函数题

静态成员和友元

判断题

选择题

填空题

 运算符重载

判断题

选择题

填空题

程序填空题

函数题

字符串类string

判断题

选择题

函数题

编程题

类的继承

判断题

选择题

函数题

多继承

判断题

选择题

填空题

虚函数

判断题

选择题

填空题

函数题

类模板template

判断题

选择题

函数题

STL标准模板库

判断题

选择题

编程题

I/O流

判断题

选择题

程序填空题

异常处理

判断题

选择题

函数题

编程题

复制构造函数

判断题

选择题

函数题


面对对象编程OOP是一种特殊的、设计程序的概念性方法。

面对对象编程的特性

抽象;封装和数据隐藏;多态;继承;代码的可重用性。

类定义

是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。

类声明

以数据成员的方式描述数据部分, 以成员函数的方式描述公有接口。

定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类。

类与结构之间的唯一区别是,结构的默认访问类型是public,而类是private

判断题

重载函数可以带有默认值参数,但是要注意二义性。         T

符号常量在定义时一定要初始化。                 T

函数的参数个数和类型都相同,只是返回值不同,这不是重载函数。 T

The types of arguments in a function call must match the types of the corresponding parameters in the function prototype's parameter list.                                        T 

(函数调用中的参数类型必须与函数原型的参数列表中相应参数的类型匹配)

在C++语言中引入内联函数(inline function)的主要目的是降低空间复杂度,即缩短目标代码长度。                                         F

引入内联函数的目的是为了解决程序中函数调用的效率问题

using namespace std; 这条语句的作用是将命名空间std内的所有标识符暴露在当前作用域内。 T

通过命名空间可以区分具有相同名字的函数                           T

选择题

C++对C语言作了很多改进,下列描述中()使得C语言发生了质变,从面向过程变成了面向对象。

A.增加了一些新的运算符;

B.允许函数重载,并允许设置缺省参数;

C.规定函数说明必须用原型;

D.引进了类和对象的概念;

如果类定义中没有使用 private、protected、或public 关键字,则所有成员( )
A.都是 public 成员
B.都是 proctected 成员
C.都是 private 成员
D.不一定

以下程序中,new语句干了什么。

int** num;

num = new int* [20];

A.分配了长度为20的整数数组空间,并将首元素的指针返回。

B.分配了一个整数变量的空间,并将其初始化为20。

C.分配了长度为20的整数指针数组空间,并将num[0]的指针返回。

D.存在错误,编译不能通过。

对定义重载函数的下列要求中,( )是错误的。

A.要求参数的个数不同

B.要求参数中至少有一个类型不同

C.要求函数的返回值不同

D.要求参数个数相同时,参数类型不同

三角形面积

p = (a+b+c)/2;

s = sqrt(p*(p-a)*(p-b)*(p-c));

以下程序存在的问题是:

void fun()

{

    int *num1, *num2;

    num1 = new int[10];

    num2 = new int[20];

    num1[0] = 100;

    num2[0] = 300;

    num1 = num2;

    delete [] num1;

}

A.num2不能给num1赋值

B.num2最初指向的空间没有释放

C.num1最初指向的空间没有释放

D.程序没有问题

设void f1(int * m,long & n);int a;long b;则以下调用合法的是()。
A.f1(a,b);
B.f1(&a,b);
C.f1(a,&b);
D.f1(&a,&b);

一个函数功能不太复杂,但要求被频繁调用,可选用( )。

A.内联函数

B.重载函数

C.递归函数

D.嵌套函数

在( )情况下适宜采用inline定义内联函数。

A.函数体含有循环语句

B.函数体含有递归语句

C.函数代码少、频繁调用

D.函数代码多、不常调用

cout 是由I/O 流库预定义的( )。
A.类
B.对象
C.包含文件
D.常量

在C++中,cin是()。
A.预定义的类
B.预定义的函数
C.一个标准的语句
D.预定义的对象

下面说法正确的是()。

A.内联函数在运行时是将该函数的目标代码插入每个调用该函数的地方

B.内联函数在编译时是将该函数的目标代码插入每个调用该函数的地方

C.类的内联函数必须在类体内定义

D.类的内联函数必须在类体外通过加关键字inline定义

命名空间应用于:

A.在类外定义类的成员函数

B.避免各个不同函数、变量等的名称冲突

C.提高代码的执行速度

D.以上答案都正确

如果在函数中定义的局部变量与命名空间中的变量同名时,()被隐藏。

A.函数中的变量

B.命名空间中的变量

C.两个变量都

D.两个变量都不

如果程序中使用了using命令同时引用了多个命名空间,并且命名空间中存在相同的函数,将出现:

A.编译错误

B.语法错误

C.逻辑错误

D.无法判定错误类型

如果默认参数的函数声明为“ void fun(int a,int b=1,char c='a',float d=3.2);”,

则下面调用写法正确的是( )。

A.fun();

B.fun(2,3);

C.fun(2, ,'c',3.14)

D.fun(int a=1);

给定以下类声明,哪个成员函数可能改变成员变量data?

class A {

public:

void f1 (int d);

void f2 (const int &d);

void f3 (int d) const;

private:

int data;

};

A.f1

B.f2

C.f3

D.f1和f2

在面向对象的软件系统中,不同类对象之间的通信的一种构造称为_______。

A.属性

B.封装

C.类

D.消息

关于成员函数特征的描述中,( )是错误的。

A.成员函数可以重载

B.成员函数一定是内联函数

C.一个类可以没有成员函数

D.成员函数可以设置参数的默认值

在面向对象系统中,对象是基本的运行时实体,它 _____ 。

A.只能包括数据(属性)

B.只能包括操作(行为)

C.把属性和行为封装为一个整体

D.必须具有显式定义的对象名

在面向对象系统中,对象的属性是________。

A.对象的行为特性

B.和其他对象相关联的方式

C.和其他对象相互区分的特性

D.与其他对象交互的方式

填空题

A pointer allows us to pass potentially large amounts of data around at low cost: instead of copying the data we simply pass its address as a pointer value. The type of the pointer determines what can be done to the data through the pointer.

(指针允许我们以低成本传递潜在的大量数据:我们只需将其地址作为指针值来传递,而不是复制数据。指针的类型决定了可以通过指针对数据执行什么操作。)

Like a pointer, a reference is an alias for an object, is usually implemented to hold a machine address of an object, and does not impose performance overhead compared to pointers, but it differs from a pointer in that:

• You access a reference with exactly the same syntax as the name of an object.

• A reference always refers to the object to which it was initialized.

• There is no "null reference," and we may assume that a reference refers  to an object

(与指针一样,引用是对象的别名,通常用于保存对象的机器地址,与指针相比不会带来性能开销,但它与指针的不同之处在于:

•您使用与对象名称完全相同的语法访问引用。

•引用总是指它初始化到的对象

•不存在“空引用”,我们可以假设引用引用了一个对象)

A reference is an alternative name for an object, an alias. The main use of references is for specifying argument and return values for functions in general and for overloaded operators in particular 

(引用是对象的替代名称,别名。引用的主要用途是为函数(尤其是重载运算符)指定参数和返回值)

A class is a user-defined type.A class consists of a set of members. The most common kinds of members are data members and member functions .A class is a namespace containing its members.

(类是用户定义的类型。类由一组成员组成。最常见的成员类型是数据成员和成员函数。类是包含其成员的命名空间。)

C++ classes are a tool for creating new types that can be used as conveniently as the built-in type.The fundamental idea in defining a new type is to separate the incidental details of the implementations (e.g., the layout of the data used to store an object of the type) from the properties essential to the correct use of it (e.g., the complete list of functions that can access the data). Such a separation is best expressed by channeling all uses of the data structure and its internal housekeeping routines through a specific interface.

(C++类是一种创建新类型的工具,可以像内置类型一样方便地使用。定义新类型的基本思想是将实现的附带细节(例如,用于存储该类型对象的数据布局)与正确使用它所必需的属性(例如,可以访问数据的函数的完整列表)分开。这种分离最好通过特定接口引导数据结构及其内部内务处理例程的所有使用来表达。)

The public members provide the class’s interface and the private members provide implementation details. Members are accessed using . (dot) for objects and −> (arrow) for pointers.

(公共成员提供类的接口,私有成员提供实现细节。使用访问成员  .(点)示对象,−>(箭头)表示指针。

A struct is a class where members are by default public.

(结构是成员默认为公共的类)

程序填空题

填空使下列程序完整并能正确运行。

#include <iostream>
using namespace std;

int main ( )
{
  double* pDouble  = NULL; // initialized with null
  pDouble  = new doube;   // Request memory for the variable
    
  char* pChar  = NULL;   // initialized with null
  pChar  = new char[20];   // Request memory for the array
    
  cin>>*pDouble >>pChar;     // Store value at allocated address
  cout << *pDouble<<endl << pChar<<endl ;
    
  delete pDouble;         // free up the memory.
  
  delete [] pChar;       // free the memory pointed to by pChar
    
  return 0;
}

构造函数与析构函数

构造函数

是一种特殊的类成员函数,在创建类时被调用。构造函数的名称和类名相同,但通过函数重载,可以创建多个同名的构造函数,条件是每个函数的特征标(参数列表)都不同。构造函数没有声明类型。

析构函数

当对象被删除时,程序将调用析构函数。每个类只能有一个析构函数。析构函数没有返回类型(连 void 都没有),也没有参数,其名称为类名前加上~。

判断题

C++程序中,类的构造函数名与类名相同。   T

对于有返回值的return语句,用它可以返回一个表达式的值,从而实现函数之间的信息传递        T

形参 int fun(int a=1,int b,int c=2)合法     F

注:默认值应该都在右边

Destructors(析构函数) can not be overloaded.      T

选择题

下列对重载函数的描述中,( )是错误的。

A.重载函数中不允许使用默认参数

B.重载函数中编译根据参数表进行选择

C.不要使用重载函数来描述毫无相干的函数

D.构造函数重载将会给初始化带来多种方式

在下面类声明中,关于生成对象不正确的是( )。

class point

{ public:

int x;

int y;

point(int a,int b) {x=a;y=b;}

};

A.point p(10,2);

B.point *p=new point(1,2);

C.point *p=new point[2];

D.point *p[2]={new point(1,2), new point(3,4)};

设A为自定义类,现有普通函数int fun(A& x)。则在该函数被调用]时:

A.将执行复制构造函数来初始化形参x

B.仅在实参为常量时,才会执行复制构造函数以初始化形参x

C.无需初始化形参x

D.仅在该函数为A类的友元函数时,无需初始化形参x

析构函数可以返回:

A.指向某个类的指针

B.某个类的对象

C.状态信息表明对象是否被正确地析构

D.不可返回任何值

建立一个类对象时,系统自动调用

A.构造函数

B.析构函数

C.友元函数

D.成员函数

下列各类函数中,不是类的成员函数的是

A.构造函数

B.析构函数

C.友元函数

D.拷贝构造函数

所有类都应该有:

A.构造函数

B.析构函数

C.构造函数和析构函数

D.以上答案都不对

以下说法中正确的是

A.一个类一定会有无参构造函数

B.构造函数的返回值类型是void

C.一个类只能定义一个析构函数,但可以定义多个构造函数

D.一个类只能定义一个构造函数,但可以定义多个析构函数

下列关于构造函数的描述中,错误的是()

A.构造函数名与类名相同

B.构造函数可以有返回值

C.构造函数可以重载

D.每个类都有构造函数

填空题

类是对象的抽象,而一个对象则是其对应的一个实例

在面向对象程序设计方法中,对象是系统中用来描述客观事物的一个实体,它由数据和可执行的一组操作共同组成。

程序填空题

阅读下列程序并填空:

#include <iostream>
#include <cstring>
using namespace std;
class Book {
    private:
        char* title; //书名
        char* author; //作者
        int sales;    //销售量
    public:
        Book();   //默认构造函数;
        Book(char *a,char *b,int c); //构造函数
        void print();         //输出函数
        ~Book();            //析构函数
};
Book::Book(char *a,char *b,int c) {
    title=new char[strlen(a)+1];
    strcpy(title,a);
    author=new char[strlen(a)+1];
    strcpy(author,b);
    sales=c;
}
void Book::print() {
    cout<<title<<endl;
    cout<<author<<endl;
    cout<<sales<<endl;
}
Book::~Book( ) {

    delete title;
    delete author;
}
int main( ) {
    char title[80],author[80];
    int sales;
    cin.getline(title,79,'\n');
    cin.getline(author,79,'\n');
    cin>>sales;
    Book aBook(title,author,sales);
    aBook.print();
}

阅读下面的程序,完成其中复制构造函数的代码。

#include <iostream>
using namespace std;
class CAT {
    public:
        CAT();
        CAT(const CAT&);
        ~CAT();
        int GetAge() const {
            return *itsAge;
        }
        void SetAge(int age) {
            *itsAge=age;
        }
    protected:
        int* itsAge;
};
CAT::CAT() {
    itsAge=new int;
    *itsAge =5;
}
CAT::CAT(const CAT& c) {
    itsAge = new int;
    *itsAge= *(c.itsAge);

}
CAT::~CAT() {                        //析构函数
    delete itsAge;
}

函数题

6-3 学生排名表(析构函数)

现在输入一批学生(人数大于0且不超过100)的名次和他们的姓名。要求按名次输出每个人的排名。

输入格式:每行为一个学生的信息,共两项,第一项为排名(为正整数,且任意两名学生的排名均不同),第二项为学生姓名。当输入-1时,表示输入结束。

输出格式:按名次输出学生姓名,每行一个。

函数接口定义:

main函数的一部分。

裁判测试程序样例:

#include <iostream>
#include <string>
using namespace std;
class Student{
    int rank;
    string name;
    public:
        int getRank(){return rank;    }
        Student(string name, int rank):name(name), rank(rank){    }
        ~Student(){ cout<<name<<endl;}
};
int main(){
    int rank, count=0;
    const int SIZE=100;
    Student *pS[SIZE];
    string name;
    cin>>rank;
    while(count<SIZE && rank>0){
        cin>>name;
        pS[count++]= new Student(name, rank);
        cin>>rank;
    }

/* 请在这里填写答案 */

    return 0;
}

输入样例:

1 Jack
5 Peter
2 Alice
6 Kate
52 Mike
-1

输出样例:

Jack
Alice
Peter
Kate
Mike
	for (int i = 0; i < count; i ++)
		for (int j = 0; j < count - i - 1; j ++)    //冒泡
			if (pS[j]->getRank() > pS[j + 1]->getRank())
			{
				Student *t = pS[j];
				pS[j] = pS[j + 1];
				pS[j + 1] = t;
			}
			
	for (int i = 0; i < count; i ++)
		delete pS[i];

6-4 体育俱乐部I(构造函数)

一个俱乐部需要保存它的简要信息,包括四项:名称(字符串),成立年份(整数),教练姓名(字符串)和教练胜率(0-100之间的整数)。用键盘输入这些信息后,把它们分两行输出:第一行输出名称和成立年份,第二行输出教练姓名和胜率。

裁判测试程序样例:

#include <iostream>
#include <string>
using namespace std;
class Coach{
    string name;
    int winRate;
public:
    Coach(string n, int wr){
        name=n; winRate=wr;
    }
    void show();
};
class Club{
    string name;
    Coach c;
    int year;
public:
    Club(string n1, int y, string n2, int wr);
    void show();
};
int main(){
    string n1, n2;
    int year, winRate;
    cin>>n1>>year>>n2>>winRate;
    Club c(n1,year, n2, winRate);
    c.show();
    return 0;
}

/* 请在这里填写答案 */

输入样例:

Guanzhou 2006 Tom 92

输出样例:

Guanzhou 2006
Tom 92%
Club::Club(string n1, int y, string n2, int wr):c(n2, wr)
{
	name = n1;
	year = y;
} 

void Coach::show() 
{
	cout << name << " " << winRate << "%" << endl;
}

void Club::show()
{
	cout << name << " " << year << endl;
	c.show();
}

this指针

指向用来调用成员函数的对象。(this被作为隐藏参数传递给方法)

每个成员函数(包括构造函数和析构函数)都有一个 this指针。this指针指向调用对象。

在函数的括号后面使用 const限定符将 this限定为 const,这样将不能使用  this来修改对象的值。

选择题

下列关于this指针的叙述中,正确的是

A.任何与类相关的函数都有this指针

B.类的成员函数都有this指针

C.类的友元函数都有this指针

D.类的非静态成员函数才有this指针

以下说法正确的是()。

A.在静态成员函数中可以调用同类的其他任何成员函数

B.const成员函数不能作用于非const对象

C.在静态成员函数中不能使用this指针

D.静态成员变量每个对象有各自的一份

对象数组和对象指针

声明对象数组的方法与声明标准类型数组相同

判断题

对象数组生命期结束时,对象数组的每个元素的析构函数并不会都被调用。    F

若new一个对象数组,那么用delete释放时应该写[],否则只delete一个对象(调用一次析构函数)。T

选择题

以下关于this指针的说法不正确的是()

A.const成员函数内部不可以使用this指针

B.成员函数内的this指针指向成员函数所作用的对象

C.在构造函数内部可以使用this指针

D.在析构函数内部可以使用this指针

下列关于对象数组的描述中,错误的是()

A.对象数组的下标是从零开始的

B.对象数组的数组名是一个常量指针

C.对象数组的每个元素是同一个类的对象

D.对象数组只能赋初值,而不能被赋值

函数题

对象指针与对象数组(拉丁舞)

怡山小学毕业文艺晚会上,拉丁舞是最受欢迎的节目。不过,每年为了排练这个节目,舞蹈组都会出现一些纠纷。有些同学特别受欢迎,有些却少人问津,因此安排舞伴成为舞蹈组陈老师最头疼的问题。

为了解决这一问题,今年陈老师决定让按先男生后女生,先低号后高号的顺序,每个人先报上自己期待的舞伴,每人报两位,先报最期待的舞伴。接下来按先男生后女生,先低号后高号的顺序,依次按以下规则匹配舞伴:

(1)每个人均按志愿顺序从前到后确定舞伴。如果第一志愿匹配不成功,则考虑第二志愿。

(2)如果A的当前志愿为B,则如果B未匹配舞伴,且有以下情形之一者,A和B匹配成功:

2a) B的期待名单中A。

2b) B的期待名单中没有A,但B期待的两位舞伴均已匹配成功,所以B只能与A凑合。

输入时先输入男生数m, 接下来m行,第一项为学生的姓名,后两项为期待舞伴的编号,编号从0开始,最大为女生数减1。接下来输入女生数f,接下来f行,第一项为学生的姓名,后两项为期待舞伴的编号,编号从0开始,最大为男生数减1。

输出时按男生的编号顺序输出  姓名:舞伴姓名

注意两个姓名间有冒号隔开

函数接口定义:

Student的两个成员函数:
void printPair();
void addPair();    

裁判测试程序样例:

#include <iostream>
#include <string>
using namespace std;
const int K=2;
const int N=20;
class Student{
  string name;
  Student *welcome[K];
  Student *pair;
  public:
      void init(string &name, Student *a, Student *b) {
        this->name=name; 
        welcome[0]=a;
        welcome[1]=b;
        pair=NULL;
      }
     void printPair();
     void addPair();        
};

/* 请在这里填写答案 */

int main(){
    Student male[N], female[N];
    int m, f, i, j, a, b;
    string name;
    cin>>m;
    for(i=0;i<m;i++){
      cin>>name>>a>>b;
      male[i].init(name, &female[a], &female[b]);
    }
    cin>>f;
    for(i=0;i<f;i++){
      cin>>name>>a>>b;
      female[i].init(name, &male[a], &male[b]);
    }
    for(i=0;i<m;i++) male[i].addPair();
    for(i=0;i<f;i++) female[i].addPair();
    for(i=0;i<m;i++) male[i].printPair();
    return 0;
}

输入样例:

5
M0 3 1
M1 1 3
M2 1 4
M3 3 1
M4 0 3
5
F0 0 2
F1 2 0
F2 2 1
F3 2 4
F4 3 2

输出样例:

M0:F1
M2:F4
M4:F0
void Student::addPair() 
{
	if (this->pair != NULL) return;    //该学生已匹配到舞伴
	
	for (int i = 0; i < 2; i ++)    //遍历该同学的期待舞伴
	{
		if (this->welcome[i]->pair != NULL) continue;//该学生期待舞伴已匹配到舞伴
		for (int j = 0; j < 2; j ++)    //遍历该期待舞伴的期待舞伴
		{
			if (this->welcome[i]->welcome[j] == this)  //匹配成功则记录
			{
				this->pair = this->welcome[i];
				this->welcome[i]->pair = this;
				return;
			}
		}
		if (this->welcome[i]->welcome[0]->pair != NULL && this->welcome[i]->welcome[1]->pair != NULL)//该期待舞伴的期待舞伴都已匹配到舞伴
		{
			this->pair = this->welcome[i];
			this->welcome[i]->pair = this;
			return;
		}
	}
}

void Student::printPair() {
	if (this->pair != NULL)
		cout << this->name << ":" << this->pair->name << endl;
}

静态成员和友元

静态成员特点

无论创建了多少对象,程序都只创建一个静态类变量副本,即类的所有对象共享同一个静态成员。

静态成员初始化

不能再类声明中初始化静态成员变量,因为声明描述如何分配内存,但并不分配内存。

在类声明外,初始化语句提出类型,并使用作用域运算符,但不使用 static 关键字。

静态成员函数

和静态成员变量一样,静态成员函数是类的一部分,而不是对象的一部分。和静态成员一样,静态成员函数也是属于类的,它并不属于任何对象,当调用静态成员函数时应该使用类名和域运算符“∷”,当然也可以使用对象调用操作,但是这样的操作并不意味着静态成员函数属于这个对象,它只是被这个对象共享而已,这样也就决定了静态成员函数中是不能访问本类中的非静态数据成员的,它是不能访问非静态数据成员的,在c++中静态成员函数主要用来访问静态数据成员而不访问非静态数据成员

静态成员函数初始化

与静态成员初始化相同,在类中声明函数的前面加static就成了静态成员函数。

可以在类声明中定义函数

在类声明外定义函数时,使用作用域运算符,但不使用 static 关键字。

友元定义

非成员函数可访问类的私有成员。

类的友元函数是非成员函数,其访问权限与成员函数相同。

在为类重载二元运算符时(带两个参数的运算符)常常需要友元。

实例见下面运算符重载

友元创建

第一步将其原型放在类声明中,并在原型声明前加上关键字 friend。

第二步编写函数定义。因为它不是成员函数,所以不要使用 类:: 限定符。另外不要再定义中使用关键字 friend。

注:友元没有悖于OOP

只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有成员。类方法和友元只是表达类接口的两种不同机制。

判断题

静态成员的特点是不管这个类创建了多少个对象,其静态成员在内存中只保留一份副本,这个副本为该类的所有对象共享,或者说静态成员为类所有。                 T

静态数据成员不能在类中初始化,使用时需要在类体外声明。                 T

静态成员变量的访问控制权限没有意义,静态成员变量均作为公有成员使用。     F

一个类的友元函数可以访问该类的私有成员。        T

如果A是B的友元类,那么B的成员函数可以访问A的私有成员。         F

对静态数据成员初始化可以在类内进行。  F

静态数据成员不属于某个对象,在给对象分配存储空间时,不包括静态数据成员所占的空间。  T

静态成员函数属于类而不是类的对象,没有this指针,静态成员函数中不能使用this指针。 T

静态成员函数的实现必须在类体外实现,不能在类体内实现。          F

由于静态成员函数不属于某个特定的对象,因此。不能像一般的成员函数那样随意的访问对象中的非静态数据成员。只能引用类中声明的静态数据成员。如果要引用非静态数据成员,可通过对象引用。                                                                                          T        

常数据成员的值必须初始化,且不能改变。     T  

常成员函数是指由const修饰符修饰的成员函数,在常成员函数中不得修改类中的任何数据成员的值。

常成员函数既可以被常对象调用,也可以被非常对象调用。      T 

选择题

下面关于友元的描述中,错误的是:
A.友元函数可以访问该类的私有数据成员
B.一个类的友元类中的成员函数都是这个类的友元函数
C.友元可以提高程序的运行效率
D.类与类之间的友元关系可以继承

关于友元函数的描述中,错误的是?
A.友元函数不是成员函数
B.友元函数必须在类体内定义
C.友元函数可以访问类的私有数据成员
D.友元函数破坏了类的封装

引入友元的主要目的是为了
A.增强数据安全性
B.提高程序的可靠性
C.提高程序的效率和灵活性
D.保证类的封装性

下面程序的输出结果是

#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass() {
        ++count;
    }
    ~MyClass() {
        --count;
    }
    static int getCount() {
        return count;
    }
private:
    static int count;
};
int MyClass::count = 0;   //静态数据成员不能在类中初始化,使用时需要在类外声明

int main() {
    MyClass obj;
    cout << obj.getCount();
    MyClass obj2;
    cout << MyClass::getCount();    //使用类名使用静态数据成员
    cout << obj2.getCount();
    return 0;
}

A.121
B.232
C.221
D.122

静态成员的特点是不管这个类创建了多少个对象,其静态成员在内存中只保留一份副本,这个副本为该类的所有对象共享,或者说静态成员为类所有。

静态数据成员不能在类中初始化,使用时需要在类体外声明。

下面对静态数据成员的描述中正确的是()。
A.类的不同对象有不同的静态数据成员值
B.静态数据成员是类的所有对象共享的数据
C.类的每个对象都有自己的静态数据成员
D.静态数据成员不能通过类的对象调用

在下面有关静态成员函数的描述中,正确的是()。
A.在建立对象前,就可以为静态数据成员赋值
B.静态成员函数在类外定义时要用static前缀
C.静态成员函数只能在类外定义
D.在静态成员函数中可以使用this指针

静态成员函数没有:
A.返回值
B.this指针
C.指针参数
D.返回类型

对于以下关于友元的说法

A.如果函数fun被声明为类A的友元函数,则该函数成为A的成员函数
B.如果函数fun被声明为类A的友元函数,则该函数能访问A的保护成员,但不能访问私有成员
C.如果函数fun被声明为类A的友元函数,则fun的形参类型不能是A。
D.以上答案都不对

A.友元函数是指某些虽然不是类成员函数却能够访问类的所有成员的函数。

B.类授予它的友元特别的访问权,这样该友元函数就能访问到类中的所有成员。

C.如果函数fun被声明为类A的友元函数,则fun的形参类型可以是A。

 填空题

A variable that is part of a class, yet is not part of an object of that class, is called a static member. There is exactly one copy of a static member instead of one copy per object, as for ordinary non-static members.

(一个变量是一个类的一部分,但不是该类的对象的一部分的,称为静态成员。一个静态成员只有一个副本,而不是像普通的非静态成员那样,每个对象有一个副本。)

静态数据成员初始化必须在类外进行。

除了可以通过对象名来引用静态成员,还可以使用类名引用静态成员。

下面程序运行结果为   3

#include <iostream>
#include <string.h>
using namespace std;
class Toy
{
        public:
            Toy(char* _n) { strcpy (name,_n); count++;}
            ~Toy(){ count--; }
            char* GetName(){ return name; }
            static int getCount(){ return count; }
        private:
            char name[10];
            static int count;
};
int Toy::count=0;
int main()
{
        Toy t1("Snoopy"),t2("Mickey"),t3("Barbie");     通过对象引用静态成员
        cout<<t1.getCount()<<endl;
        return 0;
}

下面程序的运行结果。

#include <iostream>
using namespace std;
class B
{
    public:
        B(){cout<<++b<<endl;}
        ~B(){cout<<--b<<endl;}
        static int Getb(){return b;}
    private:
        static int b;
};
int B::b=10;
int main()
{
    B b1,b2,b3;
    cout<<B::Getb()<<endl;       //使用类名引用静态成员
    return 0;
}

程序结果为:

11

12

13

13                注:该行由类名引用静态成员B::Getb()输出

12

11

10

 运算符重载

运算符重载定义

就是让原本已经存在的运算符有了新的用法和意义。

运算符重载形式
[返回类型]  operator [运算符]  (参数...) { ... };

重载<<运算符,必须使用友元函数

实例见下程序填空题

判断题

使用提取符(<<)可以输出各种基本数据类型的变量的值,也可以输出指针值      T

对每个可重载的运算符来讲,它既可以重载为友元函数,又可以重载为成员函数,还可以重载为非成员函数。                                                                F

不同的运算符不一样,有些运算符只能作为成员函数重载,见下表

对单目运算符重载为友元函数时,可以说明一个形参。而重载为成员函数时,不能显式说明形参.                                                                             T 

In C++, only existing operators can be overloaded.                         T 

(C++中只有现有的运算符可以重载)

重载operator+时,返回值的类型应当与形参类型一致。                                   F
比如以下程序中,operator+的返回值类型有错:

class A {

int x;
public:

 A(int t=0):x(t){     }

    int operator+(const A& a1){ return x+a1.x;  }
};

选择题

下列运算符中,( )运算符不能重载。
A.&&
B.[ ]
C.::
D.<<

下列关于运算符重载的描述中,( )是正确的。
A.运算符重载可以改变操作数的个数
B.运算符重载可以改变优先级
C.运算符重载可以改变结合性
D.运算符重载不可以改变语法结构

为了能出现在赋值表达式的左右两边,重载的"[]"运算符应定义为:
A.A operator [ ] (int);
B.A& operator [ ] (int);
C.const A operator [ ] (int);
D.以上答案都不对

返回类型是A &,这意味着该函数返回A对象的引用

能用友元函数重载的运算符是()。
A.+
B.=
C.[]
D.->

(友元函数是非成员函数)

 注:以上表中大多数的运算符都可以通过成员函数或者非成员函数进行重载,但以下运算符只能通过成员函数进行重载。

=  赋值运算符

()  函数调用运算符

[]  下标运算符

->  通过指针访问类成员的运算符

填空题

A binary operator can be defined by either a non-static member function taking one argument or a nonmember function taking two arguments.

(二元运算符可以由采用一个参数的非静态成员函数或采用两个参数的非成员函数定义。)

A unary operator, whether prefix or postfix, can be defined by either a non-static member function taking no arguments or a nonmember function taking one argument.

(一元运算符,无论是前缀还是后缀,都可以由不带参数的非静态成员函数或带一个参数的非成员函数定义。)

The name of an operator function is the keyword operator followed by the operator itself, for example, operator<<. An operator function is declared and can be called like any other function. A use of the operator is only a shorthand for an explicit call of the operator function.

(运算符函数的名称是关键字运算符,后面跟运算符本身,例如运算符<<。运算符函数是声明的,并且可以像任何其他函数一样被调用。运算符的使用只是运算符函数的显式调用的简写。)

程序填空题

#include<iostream>

using namespace std;
class MyInteger {

	friend ostream& operator<<(ostream& out, MyInteger myint);

	public:
		MyInteger() {
			m_Num = 0;
		}
		//前置++
		MyInteger& operator++() {
			//先++
			m_Num++;
			//再返回
			return *this;
		}
		
		//后置++
		MyInteger operator++(int) {
			//先返回

			MyInteger temp = *this;
			//记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;
			m_Num++;

			return temp;
		}
	private:
		int m_Num;
};


ostream& operator<<(ostream& out, MyInteger myint) {
	out << myint.m_Num;
	return out;
}
/*返回类型是ostream &, 即函数返回ostream对象的引用,
因为函数开始执行时,程序传递了一个对象引用给它,
所以,函数的返回值就是传递给它的对象。*/


//前置++ 先++ 再返回
void test01() {
	MyInteger myInt;
	cout << ++myInt << endl;
	cout << myInt << endl;
}

//后置++ 先返回 再++
void test02() {

	MyInteger myInt;
	cout << myInt++ << endl;
	cout << myInt << endl;
}

int main() {

	test01();
	//test02();

	system("pause");

	return 0;
}

函数题

6-1 使用成员函数重载复数类的运算符+

类Complex声明了一个复数类,有两个数据成员realPart(代表复数的实部)和imgPart(代表复数的虚部),并定义了成员函数实现了重载运算符“+”以实现两个复数对象的相加操作。成员函数Show用来输出复数的实部和虚部。请完成对运算符“+”的重载操作。

函数接口定义:
Complex& Complex::operator+(Complex& com);
参数com为复数类Complex的对象的引用,函数的返回值为当前对象与com对象相加后的值。

裁判测试程序样例:

#include<iostream>
using namespace std;

class Complex {
public:
    Complex(double realPart = 0, double imgPart = 0) {
        this->realPart = realPart;
        this->imgPart = imgPart;
    }
    Complex& operator+(Complex& com);
    void Show() {
        cout << realPart << " " << imgPart << endl;
    }
private:
    double realPart, imgPart;
};
int main() {
    int r1, i1;            //第1个复数对象的实部和虚部
    int r2, i2;            //第1个复数对象的实部和虚部
    cin >> r1 >> i1;
    cin >> r2 >> i2;
    Complex c1(r1, i1);    //构造第1个复数对象c1
    Complex c2(r2, i2);    //构造第2个复数对象c2
    c1 = c1 + c2;
    c1.Show();

    return 0;
}
/* 你的代码将被嵌在这里 */

输入样例:

3 4
10 20

输出样例:

13 24
Complex  &Complex :: operator+(Complex& com) 
{
    realPart += com.realPart;
    imgPart += com.imgPart;
    return *this;
}

6-2 单目运算符重载(时钟类)

本题已给出时钟类及其成员函数实现,要求补充完整运算符++重载函数(前置和后置),使之能够实现时钟对象自增1秒。

时钟类定义如下:

class Clock {
    public:
        Clock(int NewH=0, int NewM=0, int NewS=0);
        void ShowTime();
        friend Clock operator++(Clock& op);         //前置单目运算符重载
        friend Clock operator++(Clock& op,int);     //后置单目运算符重载
    private:
        int Hour, Minute, Second;
};

裁判测试程序样例:

#include<iostream>
using namespace std;

class Clock {
    public:
        Clock(int NewH=0, int NewM=0, int NewS=0);
        void ShowTime();
        friend Clock operator++(Clock& op);         //前置单目运算符重载
        friend Clock operator++(Clock& op,int);     //后置单目运算符重载
    private:
        int Hour, Minute, Second;
};

Clock::Clock(int NewH, int NewM, int NewS) {
    Hour=NewH;
    Minute=NewM;
    Second=NewS;
}
void Clock::ShowTime() {
    cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}

/*---------请在这里填写答案-----------*/

int main() {
    int h, m, s;
    cin>>h>>m>>s;
    Clock a(h,m,s);

    (++a).ShowTime();
    (a++).ShowTime();
    a.ShowTime();

    return 0;
}

输入样例:

在这里给出一组输入。例如:

10 10 10

输出样例:

在这里给出相应的输出。例如:

10:10:11
10:10:11
10:10:12
Clock operator ++(Clock &op) 
{
    op.Second++;
    if (op.Second >= 60) 
    {
        op.Second = op.Second - 60;
        op.Minute++;
        if (op.Minute >= 60) 
        {
            op.Minute = op.Minute - 60;
            op.Hour++;
            op.Hour = op.Hour % 24;
        }
    }
    return op;
}
Clock operator ++(Clock &op, int) 
{
    Clock OP = op;
    op.Second++;
    if (op.Second >= 60) 
    {
        op.Second = op.Second - 60;
        op.Minute++;
        if (op.Minute >= 60) 
        {
            op.Minute = op.Minute - 60;
            op.Hour++;
            op.Hour = op.Hour % 24;
        }
    }
    return OP;
}

6-2 对学生对象按照成绩升序排序

下面这段代码要实现对学生对象按照成绩升序排序。
仔细阅读代码,要求实现编程实现输出运算符“<<”和小于“<”运算符,使本程序能完成预定的排序功能。

裁判测试程序样例:

#include <iostream>
#include <string>
#include <list>
using namespace std;

class Student {
   string name;
   char sex;
   int score;
   string grade;

public:
   Student(string name, char sex, int score, string grade);
   friend ostream &operator<< (ostream& os, Student st) ;
   friend bool operator<(Student &st1, Student &st2);    
};
//你提交的代码将被嵌入到这里

Student::Student(string name, char sex, int score, string grade) {
   this->name = name;
   this->sex = sex;
   this->score = score;
   this->grade = grade;
}

int main() {
   list<Student> st;
   string name, grade;
   char sex;      int score;
    
   for(int i = 0; i < 5; i++) {
      cin>>name;      cin>>sex;
      cin>>score;       cin>>grade;
      st.push_back(Student(name, sex, score, grade));
   }

   st.sort();

   list<Student>::iterator p = st.begin();
   while(p != st.end()) {
      cout<<*p;
      p++;
   }
   return 0;
}

输入样例:

Bill M 86 JK1501
David M 98 JK1502
Mary F 78 JK1503
Adam M 83 JK1504
Rose F 96 JK1505

输出样例:

Mary F 78 JK1503
Adam M 83 JK1504
Bill M 86 JK1501
Rose F 96 JK1505
David M 98 JK1502
ostream &operator<< (ostream& os, Student st)
{
    os << st.name << " " << st.sex << " " << st.score << " " << st.grade << endl;
    return os;
}
bool operator < (Student &st1, Student &st2)
{
     if(st1.score < st2.score)
        return true;
     return false;
}

6-1 学生成绩的输入和输出

现在需要输入一组学生的姓名和成绩,然后输出这些学生的姓名和等级。

输入时,首先要输入学生数(正整数)N。接着输入N组学生成绩,每组成绩包括两项:第一项是学生姓名,第二项是学生的成绩(整数)。

输出时,依次输出各个学生的序号(从1开始顺序编号),学生姓名,成绩等级(不小于60为PASS,否则为FAIL)

函数接口定义:
面向Student类对象的流插入和流提取运算符
裁判测试程序样例:

#include <iostream>
#include <string>
using namespace std;

/* 请在这里填写答案 */

int main(){
    int i, repeat;
    Student st;
    cin>>repeat;
    for(i=0;i<repeat;i++){
        cin>>st;
        cout<<st<<endl;
    }
    return 0;
}

输入样例:

3
Li 75
Zhang 50
Yang 99

输出样例:
 

1. Li PASS
2. Zhang FAIL
3. Yang PASS
class Student{
public :
    Student (string ,int);

    friend istream & operator >>(istream&,Student &);
    friend ostream & operator <<(ostream&,Student &);
private:
    string name;
    int score;
};


Student::Student( string name="def",int score=0){
    
}

istream &operator >>(istream & is ,Student &s )
{
    is >> s.name >> s.score;
    return is;
}

ostream &operator <<(ostream & os ,Student &s)
{
    static int sum = 0;
    os << ++ sum << ". " << s.name << " ";
    if (s.score >= 60) os << "PASS";
    else os << "FAIL";

    return os;
}

字符串类string

string的大小

size() 和 length():返回string对象的字符个数,他们执行效果相同

string的比较

C ++字符串支持常见的比较操作符(>,>=,<,<=,==,!=),在使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一比较。

string的插入

push_back() 尾插一个字符

insert(pos,char): 在给定的位置pos前插入字符char

string拼接字符串:append()  和 + 操作符

string的删除:erase()

string的字符替换

replace(size_t pos, size_t n, const char *s) 将当前字符串从pos索引开始的n个字符,替换成字符串s

string的大小写转换:tolower() 和 toupper()函数

string的查找

find()查找第一次出现的目标字符串(全匹配)

find_first_of() 正向查找在原字符串中第一个与指定字符串(或字符)中的某个字符匹配的字符,返回它的位置。若查找失败,则返回npos。(npos定义为保证大于任何有效下标的值。(非全匹配)

find_last_of() 这个函数与find_first_of()功能差不多,只不过find_first_of()是从字符串的前面往后面搜索,而 find_last_of()是从字符串的后面往前面搜索(非全匹配)

rfind() 反向查找字符串,即找到最后一个与子串匹配的位置(全匹配)(从前往后搜索)

find_first_not_of() 找到第一个不与子串匹配的位置(非全匹配)

string的排序:sort(s.begin(), s.end())

string的分割/截取字符串:strtok() 和 substr()

stringvar.substr(start , length)

表示从字符串start的位置开始截取length长度的字符

判断题

get()函数不能从流中提取终止字符,终止字符仍留在流中。getline()函数可以从流中提取终止字符,但终止字符被丢弃。                                                T 

选择题

下列String类的( )方法返回指定字符串的一部分。
A.extractstring()
B.substring()
C.Substring()
D.Middlestring()

String.substring(startend)   (JAVA)

参数

start:指明子字符串的起始位置,该索引从 0 开始起算。

end:指明子字符串的结束位置,该索引从 0 开始起算。

说明 substring 方法将返回一个包含从 start 到最后(不包含 end )的子字符串的字符串。

函数题

7-3 学号解析

川师的学号的某些位有特殊的含义,如从2016110101中可以看出该学生为2016级,就读于11系,班级为1班。根据输入的学号,利用程序进行解析,输出对应的信息。

输入格式:

一个学号

输出格式:

相关信息

输入样例:

在这里给出一组输入。例如:

2016110101

输出样例:

在这里给出相应的输出。例如:

year:2016
department:11
class:01
#include<iostream>

#include<cstring>

using namespace std;

int main()

{

    string id;

    cin >> id;

    cout << "year:" << id.substr(0,4) << endl

    << "department:" << id.substr(4,2) << endl

    << "class:" << id.substr(6,2) << endl;  

}

 编程题

7-1 字符串替换

将文本文件中指定的字符串替换成新字符串。
由于目前的OJ系统暂时不能支持用户读入文件,我们编写程序从键盘输入文件中的内容,当输入的一行为end时,表示结束。end后面有两个字符串,要求用第二个字符串替换文本中所有的第一个字符串。

输入格式:
Xi’an Institute of Posts and Telecommunications is co-designed and implemented by the People’s Government of Shaanxi Province and the Ministry of Industry and Information Technology.
The Institute is located in Xi’an, a historic city in Northwest China, famous for its magnificent ancient culture.

end (表示结束)

Institute (第一个字符串,要求用第二个字符串替换)

University (第二个字符串)

输出格式:
Xi’an University of Posts and Telecommunications is co-designed and implemented by the People’s Government of Shaanxi Province and the Ministry of Industry and Information Technology.The University is located in Xi’an, a historic city in Northwest China, famous for its magnificent ancient culture.

#include<bits/stdc++.h>

using namespace std;

int main()
{
	string temp, str1, str2, str;
	while (true)
	{
		getline(cin, temp);
		if (temp == "end") break;
		
		str += temp;	//str = str.append(temp);
		str += '\n';
	}
	
	getline(cin, str1);
	getline(cin, str2);
		
	int index = str.find(str1);
	while (index != -1)
	{
		str = str.replace(index, str1.size(), str2);
		index = str.find(str1);
	}
	
	cout << str;
		
	return 0;
}

 R7-2 分离目录路径和文件名

输入文件目录路径和文件名,要求分离成目录路径和文件名分别输出

输入格式:

例如:输入

c:\windows\winhelp.exe

输出格式:

c:\windows (目录路径)

winhelp.exe (文件名)

输入样例:

/usr/bin/man

输出样例:

/usr/bin
man
#include <bits/stdc++.h>

using namespace std;

int main()
{
    string str;
    getline(cin, str);    //需读入空格

    size_t flag = str.find_last_of("\\/");
    cout << str.substr(0, flag) << endl;
    cout << str.substr(flag + 1);
    
    return 0;
}

类的继承

类继承定义

保持已有类的特性而构造新类的过程,从已有类派生出新的类,而派生类继承了原有类的特征,包括方法。

类继承目的

实现代码的重用。

访问控制 protected

与private的区别只有在基类派生的类中,派生类的成员可以直接访问基类的保护成员,但不能访问基类的私有成员。

判断题

在protected保护继承中,对于垂直访问等同于公有继承,对于水平访问等同于私有继承。  T    

类的组合关系可以用“Has-A”描述;类间的继承与派生关系可以用“Is-A”描述。    T

面向对象程序设计的继承性鼓励程序员重用被实践验证的高质量软件。             T 

选择题

下列程序的执行结果为

#include <iostream>
using namespace std;

class A {
public:
    A() {     cout << "1";    }
    ~A() {    cout << "2";    }
};
class B: public A {
public:
    B() {    cout << "3";    }
    ~B() {    cout << "4";    }
};
int main() {
    B b;
    return 0;
}

A.1234
B.1324
C.1342
D.3142

下列关于派生类构造函数和析构函数的说法中,错误的是
A.派生类的构造函数会隐含调用基类的构造函数
B.如果基类声明了带有形参表的构造函数,则派生类就应当声明构造函数
C.在建立派生类对象时,先调用基类的构造函数,再调用派生类的构造函数
D.在销毁派生类对象时,先调用基类的析构函数,再调用派生类的析构函数

在销毁派生类对象时,先调用派生类的析构函数,再调用基类的析构函数

建立派生类对象时, 3种构造函数分别是a(基类的构造函数)、b(成员对象的构造函数)、c(派生类的构造函数),这3种构造函数的调用顺序为
A.abc
B.acb
C.cab
D.cba

可以用p.a的形式访问派生类对象p的基类成员a, 其中a是
A.私有继承的公有成员
B.公有继承的私有成员
C.公有继承的保护成员
D.公有继承的公有成员

一个类的私有成员
A.只能被该类的成员函数访问
B.只能被该类的成员函数和友元函数访问
C.只能被该类的成员函数、友元函数和派生类访问
D.以上答案都不对

在公有继承的情况下,在派生类中能够访问的基类成员包括
A.公有成员
B.保护成员
C.公有成员、保护成员和私有成员
D.公有成员和保护成员

派生类继承基类的方式有
A.public
B.private
C.protected
D.以上都对

假设在公有派生情况下,以下说法不正确的是
A.可以将基类对象复制给派生类对象
B.可以将派生类对象的地址复制给基类指针
C.可以将派生类对象赋值给基类的引用
D.以将派生类对象赋值给基类对象

函数题

6-4 狗的继承

完成两个类,一个类Animal,表示动物类,有一个成员表示年龄。一个类Dog,继承自Animal,有一个新的数据成员表示颜色,合理设计这两个类,使得测试程序可以运行并得到正确的结果。

函数接口定义:

按照要求实现类

裁判测试程序样例:

/* 请在这里填写答案 */

int main(){
    Animal ani(5);
    cout<<"age of ani:"<<ani.getAge()<<endl;
    Dog dog(5,"black");
    cout<<"infor of dog:"<<endl;
    dog.showInfor();
}

输入样例:

输出样例:

age of ani:5
infor of dog:
age:5
color:black
#include <bits/stdc++.h>

using namespace std;

class Animal{
	private:
		int age;
	public:
		Animal(int a)
		{
			age = a;
		}
		int getAge()
		{
			return age;
		}
};

class Dog:public Animal{
	private:
		string color;
	public:
		Dog(int a, string c):Animal(a),color(c){
		}
		void showInfor()
		{
			cout << "age:" << getAge() << endl;
			cout << "color:" << color << endl;
		}
};

6-3 写出派生类构造方法(C++)

裁判测试程序样例中展示的是一段定义基类People、派生类Student以及测试两个类的相关C++代码,其中缺失了部分代码,请补充完整,以保证测试程序正常运行。

函数接口定义:

提示: 观察类的定义和main方法中的测试代码,补全缺失的代码。

裁判测试程序样例:

注意:真正的测试程序中使用的数据可能与样例测试程序中不同,但仅按照样例中的格式调用相关函数。

#include <iostream>
using namespace std;
class People{
private:
    string id;
    string name;
public:
    People(string id, string name){
        this->id = id;
        this->name = name;
    }
    string getId(){
        return this->id;
    }
    string getName(){
        return name;
    }
};
class Student : public People{
private:
    string sid;
    int score;
public:
    Student(string id, string name, string sid, int score)
        
        /** 你提交的代码将被嵌在这里(替换此行) **/
        
    }
    friend ostream& operator <<(ostream &o, Student &s);
};
ostream& operator <<(ostream &o, Student &s){
    o << "(Name:" << s.getName() << "; id:"
      << s.getId() << "; sid:" << s.sid
      << "; score:" << s.score << ")";
    return o;
}
int main(){
    Student zs("370202X", "Zhang San", "1052102", 96);
    cout << zs  << endl;
    return 0;
}

输入样例:

(无)

输出样例:

(Name:Zhang San; id:370202X; sid:1052102; score:96)
:People(id,name){
		this->sid=sid;
		this->score=score;

多继承

判断题

多重继承派生类的构造函数,需要调用所有的基类构造函数来完成各基类数据成员的初始化。   T 

选择题

在C++语言中设置虚基类的目的是( )
A.简化程序代码
B.提高程序的运行效率
C.解决多继承造成的二义性问题

D.缩短程序的目标代码

下列关于继承的描述中,错误的是( )。
A.析构函数不能被继承
B.派生类是基类的组合
C.派生类的成员除了它自己的成员外,还包含了它的基类的成员
D.派生类中继承的基类成员的访问权限到派生类保持不变

以下关于C++语言中继承的叙述中,错误的是( )。
A.继承是父类和子类之间共享数据和方法的机制
B.继承定义了一种类与类之间的关系
C.继承关系中的子类将拥有父类的全部属性和方法
D.继承仅仅允许单继承,即不允许一个子类有多个父类

填空题

A base class's protected members can be accessed only in the base-class definition or in derived-class definitions.

(基类的受保护成员只能在基类定义或派生类定义中访问。)

虚函数

虚函数的定义
在实现c++多态时会用到虚函数。虚函数使用的其核心目的是通过基类访问派生类定义的函数。所谓虚函数就是在基类定义一个未实现的函数名,为了提高程序的可读性,建议后代中虚函数都加上virtual关键字。

常见用法:声明基类指针,利用指针指向任意一个子类对象,调用相关的虚函数,动态绑定,由于编写代码时不能确定被调用的是基类函数还是那个派生类函数,所以被称为“”虚“”函数。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。

实例见下函数题6-2 解决内存泄漏问题

虚函数的常见错误 -override 和 final
虚函数的两个常见错误:无意的重写、虚函数签名不匹配。

无意的重写
无意的重写 示例如下,在派生类中声明了一个与基类的某个虚函数具有相同的签名的成员函数,不小心重写了这个虚函数。

虚函数签名不匹配

函数的签名包括:函数名,参数列表,const属性。

纯虚函数定义

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:

eg.  virtual void funtion1()=0

定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。

抽象类
包含纯虚函数的类称为抽象类。

抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。

抽象类的作用

抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。

使用抽象类时注意:

抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。
抽象类是不能定义对象的。

判断题

虚函数是用virtual 关键字说明的成员函数。               T

动态绑定是在运行时选定调用的成员函数的。              T 

构造函数可以声明为虚函数。                                 F

构造函数可以声明为纯虚函数。                              F  

虚函数不能是类的静态成员。                            T             

重定义虚函数的派生类必须是公有继承的。          T

作为虚函数隐含参数的this指针,决定了虚函数调用时执行的代码。       T

Dynamic binding is used as default binding method in C++.                 F  

(在C++中,动态绑定被用作默认的绑定方法。)

两种绑定方式
静态绑定:在编译时刻,根据指针或引用变量的静态类型来决定成员函数属于哪一个类。

动态绑定:在运行时刻,根据指针或引用变量实际指向或引用的对象类型(动态类型)来确定成员函数属于哪一个类。

C++默认的绑定方式是静态绑定。

类的构造函数可以定义为虚函数。                                 F  

选择题

关于纯虚函数和抽象类的描述中,( )是错误的。
A.纯虚函数是一种特殊的虚函数,它没有具体的实现
B.抽象类是指具有纯虚函数的类
C.一个基类中说明有纯虚函数,该基类的派生类一定不再是抽象类
D.抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出

如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。

虚析构函数的作用是。
A.虚基类必须定义虚析构函数
B.类对象作用域结束时释放资源
C.delete动态对象时释放资源
D.无意义

在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值。

A.相同

B.不同

C.相容

D.部分相同

若一个类中含有纯虚函数,则该类称为。
A.基类
B.纯基类
C.抽象类
D.派生类

假设 Aclass为抽象类,下列正确的说明语句是。
A.Aclass fun( int ) ;
B.Aclass * p ;
C.int fun( Aclass ) ;
D.Aclass Obj ;

关于动态绑定的下列描述中,( )是错误的。

A.动态绑定是以虚函数为基础的

B.动态绑定在运行时确定所调用的函数代码

C.动态绑定调用函数操作是通过指向对象的指针或对象引用来实现的

D.动态绑定是在编译时确定操作函数的

关于虚函数的描述中,( )是正确的。

A.虚函数是一个static 类型的成员函数

B.虚函数是一个非成员函数

C.基类中说明了虚函数后,派生类中与其对应的函数可不必说明为虚函数

D.派生类的虚函数与基类的虚函数具有不同的参数个数和类型

在C++语言中设置虚基类的目的是( ) 
A.简化程序代码
B.提高程序的运行效率
C.解决多继承造成的二义性问题
D.缩短程序的目标代码

填空题

Virtual functions overcome the problems with the type-field solution by allowing the programmer to declare functions in a base class that can be redefinded  in each derived class. The compiler and linker will guarantee the correct correspondence between objects and the functions applied to them.

(虚拟函数通过允许程序员在基类中声明可以在每个派生类中重新定义的函数,克服了类型字段解决方案的问题。编译器和链接器将保证对象和应用于它们的函数之间的正确对应。)

函数题

6-2 抽象类Shape

请编写一个抽象类Shape,包括两个纯虚函数,分别为计算面积getArea()和计算周长getPerim()。通过Shape类派生出矩形类Rectangle和圆类Circle,并计算各自的面积和周长。

测试用例具体要求:输入1表示测试矩形类,之后输入矩形长和宽。输入2表示测试圆类,之后输入圆半径。

Shape类定义如下:

class Shape {
    public:
        virtual double getArea()=0;
        virtual double getPerim()=0;
};

裁判测试程序样例:

#include <iostream>
using namespace std;
const double PI=3.14;

class Shape {
    public:
        virtual double getArea()=0;
        virtual double getPerim()=0;
};

/* ------请在这里填写答案 ------*/

int main() {
    Shape *p;
    int n;
    double w,h,r;
    scanf("%d",&n);
    switch(n) {
        case 1: {
            cin>>w>>h;
            Rectangle rect(w,h);
            p = &rect;
            cout<<"area="<<rect.getArea()<<endl;//等价于p->getArea()
            cout<<"perim="<<rect.getPerim()<<endl;//p->getPerim()
            break;
        }
        case 2: {
            cin>>r;
            Circle c(r);
            p = &c;
            cout<<"area="<<c.getArea()<<endl;//p->getArea()
            cout<<"perim="<<c.getPerim()<<endl;//p->getPerim()
            break;
        }
    }

    return 0;
}

输入样例1:

在这里给出一组输入。例如:

1
4 5

输出样例1:

在这里给出相应的输出。例如:

area=20
perim=18

输入样例2:

在这里给出一组输入。例如:

2
5

输出样例2:

在这里给出相应的输出。例如:

area=78.5
perim=31.4
class Rectangle : public Shape{
private:
    double width;
    double height;
public:
    Rectangle(double w, double h){
        width = w;
        height = h;
    }
    double getArea(){
        return width * height;
    }
    double getPerim(){
        return (width + height) * 2;
    }
};
class Circle : public Shape{
private:
    double radius;
public:
    Circle(double r){
        radius = r;
    }
    double getArea(){
        return PI * radius * radius;
    }
    double getPerim(){
        return (PI * radius) * 2;
    }
};

6-3 虚函数的应用

补充下列代码,使得程序的输出为:
A:3
A:15
B:5
3
15
5

类和函数接口定义:

参见裁判测试程序样例中的类和函数接口。

裁判测试程序样例:

#include <iostream>
using namespace std;
class CMyClassA {
    int val;
public:
    CMyClassA(int);
    void virtual print();
};
CMyClassA::CMyClassA(int arg) {
    val = arg;
    printf("A:%d\n", val);
}
void CMyClassA::print() {
    printf("%d\n", val);
    return;
}

/* 在这里填写代码 */

int main(int argc, char** argv) {
    CMyClassA a(3), *ptr;
    CMyClassB b(5);
    ptr = &a;
    ptr->print();
    a = b;
    a.print();
    ptr = &b;
    ptr->print();
    return 0;
}

输入样例:

None

输出样例:

A:3
A:15
B:5
3
15
5
class CMyClassB :public CMyClassA {
  public:
      int val2;
    CMyClassB(int x) :CMyClassA(3 * x), val2(x) {
        printf("B:%d\n", val2);
    }
    void print() {
        printf("%d\n", val2);
    }
};

6-2 解决内存泄漏问题

编译、运行下列程序后。从输出结果发现没有调用 class Y 的析构函数,出现了内存泄漏。请尝试修改class X类的定义解决这个内存泄露问题。并提交定义class X类的代码。

class X类的定义如下:

class X{
public:
    X() { p = new int[2]; cout << "X().    "; }
   ~X() { delete [] p; cout << "~X().\n"; }
private:
    int* p;
};
#include <iostream> 
using namespace std; 
// 你提交的代码将嵌入到这里


class Y : public X
{
public:
   Y( ) { q = new int[1023]; cout << "Y( )    "; }
   ~Y( ) { delete [] q; cout << "~Y().    "; }
private:
   int* q;
};
int main()
{
  int n;
  cin>>n; 
  for (int i = 0; i < n; i++)
  {
    X* r = new Y;
    delete r;
  }
  return 0;
}
从输出结果发现没有调用 class Y 的析构函数,出现了内存泄漏。
3
X().    Y( )    ~X().
X().    Y( )    ~X().
X().    Y( )    ~X().

输入样例:

3

输出样例:(输出显示调用了Y类的析构函数)

X().    Y( )    ~Y().    ~X().
X().    Y( )    ~Y().    ~X().
X().    Y( )    ~Y().    ~X().

使用题中给出的class A(即没有使用虚函数) 如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因此没有调用Y的析构函数

class X{
public:
    X() { p = new int[2]; cout << "X().    "; }
   
   virtual ~X() { delete [] p; cout << "~X().\n"; }//使用虚函数可以解决上述问题
private:
    int* p;
};

类模板template

类模板的作用
类模板与函数模板的定义和使用类似,有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,我们可以通过如下面语句声明了一个类模板:


类模板定义
类模板由模板说明和类说明构成

   模板说明同函数模板,如下:

         template    <类型形式参数表>

         类声明

template <typename T>
class A
{
    public:
        A(T t)
        {
            this->t = t;
        }
 
        T& getT()
        {
            return t;
        }
 
    public:
        T t;
};

(1)template:这是声明类模板的关键字,表明这是一个类模板。

(2)尖括号<>中,typename和class的作用都一样,都是用作来申明后面的参数是一个虚拟的数据参数类型。

(3)类型参数:用C++标识符:Type或者T来表示,表示这是一个虚拟的数据类型名,此时在类模板中不给定具体的数据类型,实例化成模板类后,就需要给定具体的数据类型。

单个类模板的使用

#include <bits/stdc++.h>

using namespace std;
 
template <typename T>
class A
{
	public:
		A (T t)
		{
			this->t = t;
		}
		T getT()
		{
			return t;
		}
		void print()
		{
			cout << t << endl;
		}
	private:
		T t;
};
 
int main() 
{
	//1.模板类定义类对象,必须显示指定类型
	//2.模板种如果使用了构造函数,则遵守以前的类的构造函数的调用规则
	A<char>  a('a');
	cout << a.getT() << endl;
	a.print();
	
	A<string> b("stranger");
	cout << b.getT() << endl;
	b.print();
	
	A<int> c(617);
	cout << c.getT() << endl;
	c.print();
	
	A<double> d(3.1415);
	cout << d.getT() << endl;
	d.print();

	return 0;
}

运行结果如下

判断题

pair类模板的作用是将两个数据组成一个数据,用来表示一个二元组或一个元素对,两个数据可以是同一个类型也可以是不同的类型。                          T

选择题

关于函数模板,描述错误的是。
A.函数模板必须由程序员实例化为可执行的函数模板
B.函数模板的实例化由编译器实现
C.一个类定义中,只要有一个函数模板,则这个类是类模板
D.类模板的成员函数都是函数模板,类模板实例化后,成员函数也随之实例化

下列的模板说明中,正确的是。
A.template < typename T1, T2 >
B.template < class T1, T2 >
C.template < typename T1, typename T2 >
D.template ( typedef T1, typedef T2 )

假设有函数模板定义如下:

template
Max( T a, T b ,T &c)
{ c = a + b ; }

下列选项正确的是( )。
A.int x, y; char z ;Max( x, y, z ) ;
B.double x, y, z ;Max( x, y, z ) ;
C.int x, y; float z ;Max( x, y, z );
D.float x; double y, z;Max( x, y, z ) ;

建立类模板对象的实例化过程为。
A.基类-派生类
B.构造函数-对象
C.模板类-对象
D.模板类-模板函数

模板函数的真正代码是在哪个时期产生的____。
A.源程序中声明函数时
B.源程序中定义函数时
C.源程序中调用函数时
D.运行执行函数时

类模板的模板参数____。
A.只可作为数据成员的类型
B.可作为成员函数的返回类型
C.可作为成员函数的参数类型
D.以上三者皆可

下列关于pair<>类模板的描述中,错误的是。
A.pair<>类模板定义头文件utility中
B.pair<>类模板作用是将两个数据组成一个数据,两个数据可以是同一个类型也可以是不同的类型
C.创建pair<>对象只能调用其构造函数
D.pair<>类模拟提供了两个成员函数first与second来访问这的两个数据

关于类模板,描述错误的是。
A.一个普通基类不能派生类模板
B.类模板可以从普通类派生,也可以从类模板派生
C.根据建立对象时的实际数据类型,编译器把类模板实例化为模板类
D.函数的类模板参数需生成模板类并通过构造函数实例化

下列有关模板的描述,错误的是____。
A.模板把数据类型作为一个设计参数,称为参数化程序设计
B.使用时,模板参数与函数参数相同,是按位置而不是名称对应的
C.模板参数表中可以有类型参数和非类型参数
D.类模板与模板类是同一个概念

类模板:主要描述的是模板,这个模板是类的模板。可以理解为一个通用的类,这个类中的数据成员,成员函数的形参类型以及成员函数的返回值类型不用具体的指定,这些类型都是虚拟的。在使用类模板进行对象定义的时候,才会根据对象的实际参数类型来替代类模板中的虚拟类型。

模板类:主要描述的是类,这个类使用类模板进行声明。将类模板中的虚拟类型参数指定成一个具体的数据类型参数。

函数题

6-1 一个简单的队列类模板

请按照下列简单的整数队列类创建一个简单的队列类模板。

整数队列类如下:

const int SIZE=100;
//整数队列类
class Queue {    
  int q[SIZE];
  int front;  //队列头
  int rear;   //队列尾
public:
  Queue( ) 
  { front = rear = 0; }
  void put(int i); // 在队尾放置一个数据
  int get( );  // 从队列头部取一个数据
};
 

裁判测试程序样例:

在这里给出函数被调用进行测试的例子。例如: 
#include <iostream>
#include <string>
using namespace std;
// 你提交的代码将嵌入到这里

int main()
{
  Queue<int> a; // 创建一个整数队列
  int m,n;
  cin>>m>>n; 
  a.put(m);
  a.put(n);
  cout << a.get() << " ";
  cout << a.get() << endl;

  Queue<double> d; // 创建一个双精度浮点数队列
  double x,y;
  cin>>x>>y;
  d.put(x);
  d.put(y);
  cout << d.get() << " ";
  cout << d.get() << endl;

  Queue<string> qs;// 创建一个字符串队列
  string s1,s2,s3;
  cin>>s1>>s2>>s3;
  qs.put(s1);
  qs.put(s2);
  qs.put(s3);
  cout <<    qs.get() << " ";
  cout <<    qs.get() << " ";
  cout << qs.get() << endl;

  return 0;
}

输入样例:

6 9
3.14159 2.781828
ShenZhen Beijing HangZhou

输出样例:

6 9
3.14159 2.78183
ShenZhen Beijing HangZhou
const int SIZE = 100;
template <typename T> 
class Queue {
private:
    T q[SIZE];
    int front;
    int rear;
public:
    Queue() {
        front = rear = 0;
    }
    void put(T i);
    T get();
};
template <class T> void Queue<T> :: put(T i) {
    if (front == SIZE)
        exit(0);
    q[rear ++] = i;
}
template <class T>T Queue<T> :: get() {
    if (rear == front)
        return 0;
    return q[front ++];
}

6-3 创建函数模板实现求数组中的最小元素

创建一个函数模板实现求数组中的最小元素,在主函数将分别使用该模板求整形数组和double型数组中的最小元素并输出。

输入:

6 (整型数组中的元素个数)

8 3 4 1 6 9 (整型数组中的元素)

5 (双精度浮点数数组中的元素个数)

2.7 3.1 9.8 5.3 7.6 (双精度浮点数数组中的元素)

输出:

1

2.7

函数模板接口定义:

T Min(T *p, int len)

其中 p 和 len 都是用户传入的参数。 p 是数组元素的起始地址; len 是数组的长度。

裁判测试程序样例:

#include <iostream>
using namespace std;
// 你提交的代码将嵌入到这里

int main( )
{
    int n,m,*pn,i=0;
    cin>>n;
    pn=new int[n];
    do{
        cin>>pn[i];
        i++;
    }while(i<n);
 
    double *pd;
    i=0;
    cin>>m;
    pd=new double[m];
    do{
        cin>>pd[i];
        i++;
    }while(i<m);
 
    cout<<Min(pn,n)<<endl;
    cout<<Min(pd,m)<<endl;
    delete [ ] pn;
    delete [ ] pd;
    return 0;
}

输入样例:

9
8 2 7 6 4 5 3 1 0
7
3.1 9.6 5.8 2.7 6.3 7.0 8.5

输出样例:

0
2.7
template<typename T>
T Min (T *p,int len)
{
    T min = p[0];
    for (int i = 1; i < len; i ++)
    {
        if (p[i] < min)
            min = p[i];
    }
    return min;
}

6-4 数组排序输出(函数模板)

对于输入的每一批数,按从小到大排序后输出。

一行输入为一批数,第一个输入为数据类型(1表示整数,2表示字符型数,3表示有一位小数的浮点数,4表示字符串,0表示输入结束),第二个输入为该批数的数量size(0<size<=10),接下来为size个指定类型的数据。

输出将从小到大顺序输出数据。

函数接口定义:sort函数将接受size个数据,将它们从小到大排序后存在a指向的一段连续空间中。

template <class T>
void sort(T *a, int size);

裁判测试程序样例:

#include <iostream>
#include <string>
using namespace std;

/* 请在这里填写答案 */

template <class T>
void display(T* a, int size){
    for(int i=0; i<size-1; i++) cout<<a[i]<<' ';
    cout<<a[size-1]<<endl;
}
int main() {
     const int SIZE=10;
     int a[SIZE];
     char b[SIZE];
     double c[SIZE];
     string d[SIZE];
     int ty, size;
     cin>>ty;
     while(ty>0){
         cin>>size;
         switch(ty){
             case 1:sort(a,size); display(a,size); break;
             case 2:sort(b,size); display(b,size); break;
             case 3:sort(c,size); display(c,size); break;
             case 4:sort(d,size); display(d,size); break;
         }
         cin>>ty;
     }
      return 0;
}

输入样例:

1 3 3 2 1
2 2 a A
3 3 1.5 2.6 2.2
4 2 bca abc
0

输出样例:

1 2 3
A a
1.5 2.2 2.6
abc bca
template <class T>
void sort(T *a, int n)
{
    for(int i = 0; i < n;i ++)
    {
        cin >> a[i];
    }
    for(int i = 0; i < n - 1; i ++){  //冒泡排序
        for(int j = 0; j < n - i - 1; j ++){
            if(a[j] > a[j + 1]){
                T temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}

STL标准模板库

vector 动态数组

vector和built-in数组类似,拥有一段连续的内存空间,能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当插入较多的元素后,预留内存空间可能不够,需要重新申请一块足够大的内存并把原来的数据拷贝到新的内存空间。这些影响了vector的效率,但是实际上用的最多的还是vector容器,建议大多数时候使用vector效率一般是不错的。

list 双向链表

list就是数据结构中的双向链表(根据sgi stl源代码),因此它的内存空间是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。
stack 栈

queue 队列
deque 双端队列

deque是一个double-ended queue,具有以下两个特点:它支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。
priority_queue 优先队列
map 映射

一个关联容器,以键值对存储的数据,其类型可以自己定义,每个关键字在map中只能出现一次,关键字不能修改,值可以修改;map内部有序(自动排序),查找时间复杂度为O(logn)
set 集合

自动去重并按升序排序,其内部采用“红黑树”实现
pair 二元组
string 字符串
bitset 存储二进制数位

bitset就像一个bool类型的数组一样,但是有空间优化——bitset中的一个元素一般只占1 bit,相当于一个char元素所占空间的八分之一。
bitset中的每个元素都能单独被访问,例如对于一个叫做foo的bitset,表达式foo[3]访问了它的第4个元素,就像数组一样。
bitset有一个特性:整数类型和布尔数组都能转化成bitset。
bitset的大小在编译时就需要确定。
array 数组
tuple 元组

判断题

可以通过下标随机访问向量vector中的元素。         T

当向量对象的内存用完之后,就会产生越界错误。                  F  

选择题

若有下面的语句:

vector<int> v;
for (int i = 0; i < 4; i++)
    v.push_back(i + 1);
cout << v.size() << endl;


则执行后程序的输出结果是
A.1
B.2
C.3
D.4

设有定义 vector<string> v(10);
执行下列哪条语句时会调用构造函数?
A.v[0] += "abc";
B.v[0] = "2018";
C.v.push_back("ZUCC");
D.cout << (v[1] == "def");

设有如下代码段:

std::map<char *, int> m;
const int MAX_SIZE = 100;
int main() {
    char str[MAX_SIZE];
    for (int i = 0; i < 10; i++) {
        std::cin >> str;
        m[str] = i;
    }
    std::cout << m.size() << std::endl;
}

读入10个字符串,则输出的 m.size() 为
A.0
B.1
C.10

因为std::map<char *, int> m;

该map的键为char *,即指向str的指针,因此下面循环中map的键都未改变,只是改变了该char*对应的数值。并未创建新的元素

所以m.size()为1

编程题

7-1 查找电话号码

文件phonebook1.txt中有若干联系人的姓名和电话号码。

高富帅 13312342222

白富美 13412343333

孙悟空 13512345555

唐三藏 13612346666

猪悟能 13712347777

沙悟净 13812348888

请你编写一个简单的通信录程序,当从键盘输入一个姓名时查找到对应的电话号码并输出。如果没找到则显示Not found.
由于目前的自动裁判系统暂时不能支持用户读入文件,我们编写程序从键盘输入文件中的姓名和电话号码,当输入的名字为noname时,表示结束。noname后面有一个名字,需要查找其对应的电话号码。

输入格式:

高富帅 13312342222

白富美 13412343333

孙悟空 13512345555

唐三藏 13612346666

猪悟能 13712347777

沙悟净 13812348888

noname (表示结束)

唐三藏 (需要查找此人的电话号码)

输出格式:

13612346666 (输出对应的电话号码)

输入样例:

白富美 13412343333
孙悟空 13512345555
唐三藏 13612346666
猪悟能 13712347777
沙悟净 13812348888
noname
白骨精

输出样例:

Not found.
#include <iostream>
#include <map>
using namespace std;

int main() 
{
    map<string, string> m;
    string a;
    string b;
    while(1) 
    {
        cin >> a;
        if (a == "noname")
            break;
        cin >> b;
            m.insert(pair<string, string>(a, b));
    }
    string s;
    cin >> s;
    map<string, string> :: iterator p;    //迭代器
    p = m.find(s);
    if (p != m.end())
        cout << p -> second << endl;
    else
        cout << "Not found." << endl;
    return 0;
}

7-2 电话号码同步

文件phonebook1.txt和phonebook2.txt中有若干联系人的姓名和电话号码。请你设计一个程序,将这两个文件中的电话号码同步。(所谓同步,就是将两个文件中的电话号码合并后剔除相同的人名和电话号码。请将同步后的电话号码按照姓名拼音顺序排序后保存到文件phonebook3.txt中。)

由于目前的OJ系统暂时不能支持用户读入文件和写文件,我们编写程序从键盘输入文件中的姓名和电话号码,当输入的单词为end时,表示文件结束。将同步后的电话号码按照姓名拼音顺序排序后输出。

输入格式:
张三 13012345678

李四 13112340000

王五 13212341111

马六 13312342222

陈七 13412343333

孙悟空 13512345555

end (表示文件phonebook1.txt结束)
张三 13012345678

孙悟空 13512345555

王五 13212341111

陈七 13412343333

唐三藏 13612346666

猪悟能 13712347777

沙悟净 13812348888

end (表示文件phonebook2.txt结束)

输出格式:
陈七 13412343333

李四 13112340000

马六 13312342222

沙悟净 13812348888

孙悟空 13512345555

唐三藏 13612346666

王五 13212341111

张三 13012345678

猪悟能 13712347777

输入样例:

Zhang3 13012345678
Li4 13112340000
Wang5 13212341111
Ma6 13312342222
Chen7 13412343333
SunWuKong 13512345555
end
Zhang3 13012345678
SunWuKong 13512345555
Wang5 13212341111
Chen7 13412343333
TangSanZang 13612346666
ZhuWuneng 13712347777
ShaWuJing 13812348888
end

输出样例:

Chen7 13412343333
Li4 13112340000
Ma6 13312342222
ShaWuJing 13812348888
SunWuKong 13512345555
TangSanZang 13612346666
Wang5 13212341111
Zhang3 13012345678
ZhuWuneng 13712347777
#include <bits/stdc++.h>

using namespace std;

int main() 
{
	string str;
	set<string> m;
	for (int i = 0; i < 2; i ++)
	{
		getline(cin, str);
		while (str != "end")
		{
			m.insert(str);
			getline(cin, str);
		}
	}
	
	for (set<string> ::iterator p = m.begin(); p != m.end(); p ++)
		cout << * p << endl;
	
    return 0; 
}

I/O流

C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。

为了实现这种流动, C++定义了 I/O 标准类库,这些每个类都称为流/流类,用以完成某方面的功能

标准IO流

<iostream>

对于 cin:在C++中称为流提取,也就是在 IO流 中提取信息,比如说读文件、获取键盘的信息等,使用 cin 进行标准输入即数据通过键盘输入到程序中

对于 cout:在C++中称为流插入,也就是在 IO流 中插入信息,比如说写文件、将信息输出到屏幕等,cout 进行标准输出,即数据从内存流向控制台 ( 显示器 )。

文件IO流

<fstream>

打开文件
无论是以哪种方式操作文件,都是要先创建文件流类的一个对象,然后将这个对象与文件联系起来,也就是说打开一个文件,被打开的文件在程序中由一个流对象(stream object)来表示 ,而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。
使用fstream类的成员函数open()实现文件的打开,其函数原型为:
void open (const char * filename, openmode mode);
void open (const char * filename, openmode mode,int access);

filename是一个字符串,代表要打开的文件名;mode是打开文件的方式;access打开文件的属性(基本上很少用到,使用第一种函数原型就可以)。

关闭文件
当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件,其函数原型为:void close();
这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。
为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close()。

读写文件

ofstream类:向文件写入

构造:第一个参数是文件名,第二个参数代表打开的模式
二进制写:用的接口是write()
文本写:用的是 <<

ifstream类:读取文件

构造:第一个参数是文件名,第二个参数代表打开的模式
二进制读:用的接口是read
文本读:用的是 >>

<sstream>:用来支持字符串的序列化与反序列化,多用于网络,与流关系不大

对于 stringsream:字符串常用这个

判断题

预定义的插入符从键盘上接收数据是不带缓冲区的。                 F 

记录流的当前格式化状态标志字中的每一位用于记录一种格式,这种格式是不能被设置或清除的.                          F 

设置和清除格式标志字的成员函数需要通过对象来引用它们,输出显示格式的对象通常是cout。T

操纵符本身是一个对象,它可以直接被提取符或插入符操作。             T

get()函数不能从流中提取终止字符,终止字符仍留在流中。getline()函数可以从流中提取终止字符,但终止字符被丢弃。                                        T

使用打开文件函数open()之前,需要定义一个流类对象,使用open()函数来操作该对象。    T

打开ASCⅡ码流文件和二进制流文件时,打开方式是相同的。             F 

read()和write()函数可以读写文本文件,也可以读写二进制文件。         T

seekg()函数和seekp()函数分别用来定位读指针和写指针的。如果使用seek()函数可以同时定义读写指针。                                                                                                   F 

文件流对象有两个成员函数,分别是 seekp 和 seekg。它们可以用于将读写位置移动到文件中的任何字节。

seekp 函数用于已经打开要进行输出的文件,而 seekg 函数则用于已经打开要进行输入的文件。

预定义的插入符从键盘上接收数据是不带缓冲区的。                F 

选择题

cout 是由I/O 流库预定义的( )。
A.类

B.对象
C.包含文件
D.常量

分析以下程序:程序的输出结果是

#include <iostream>
using namespace std;
void fun(int num)
{
    cout << num << endl;
}
void fun(char ch)
{
    cout << (ch + 1) << endl;
}
int main()
{
    fun('A');
    return 0;
}

A.65
B.66
C.A
D.B

注:A的ascii为65, a为97

下列关于cin和cout的说法中,错误的是____。
A.cin用于读入用户输入的数据
B.cout用于输出数据
C.cin比C语言中的scanf()函数更有优势,它可以读取空格
D.cout通常与<<运算符结合

下面是关于ios 类的叙述,正确的是( )。
A.它是istream 类和ostream 类的虚基类
B.它只是istream 类的虚基类
C.它只是ostream 类的虚基类
D.它是iostrearm 类的虚基类

关于read()函数的下列描述中,正确的是( )。
A.该函数只能用来从键盘输入中获取字符串
B.该函数所获取的字符多少是不受限制的
C.该函数只能用于文本文件的操作中
D.该函数只能按规定读取所指定的字符数

当使用ifstream 流类定义一个流对象并打开一个磁盘文件时,文件的隐含打开方式为( )。
A.ios::in
B.ios::out
C.ios::in|ios::out
D.ios::binary

下列函数中,( )是对文件进行写操作的。
A.get
B.read
C.seekg
D.put

在C++中,打开一个文件,就是将整个文件与一个( )建立关联,关闭一个文件,就是取消这种关联。
A.类
B.流
C.对象
D.结构

下列打开文件的表达式中,错误的是:
A.ofstream ofile; ofile.open(“C:\vc\abc.txt”,ios::binary);
B.fstream iofile;iofile.open(“abc.txt”,ios::ate);
C.ifstream ifile (“C:\vc\abc.txt”);
D.cout.open(“C:\vc\abc.txt”,ios::binary);

以下关于文件操作的叙述中,不正确的是:
A.打开文件的目的是使文件对象与磁盘文件建立联系
B.文件读写过程中,程序将直接与磁盘文件进行数据交换
C.关闭文件的目的之一是保证将输出的数据写入硬盘文件
D.关闭文件的目的之一是释放内存中的文件对象

使用操作符setw对数据进行格式输出时,需要包含()文件。
A.iostream.h
B.fstream.h
C.iomanip.h
D.stdlib.h

setw()函数
setw(int n)是c++中在输出操作中使用的字段宽度设置,设置输出的域宽,n表示字段宽度。只对紧接着的输出有效,紧接着的输出结束后又变回默认的域宽。
当后面紧跟着的输出字段长度小于n的时候,在该字段前面用空格补齐;当输出字段长度大于n时,全部整体输出。

程序填空题

文本文件输入输出

#include <fstream>
#include <iostream>
using namespace std;

int main () {

    char data[100];

    // 以写模式打开文件
    ofstream outfile;
    outfile.open("afile.dat");

    cout << "Writing to the file" << endl;
    cout << "Enter your name: ";

    cin.getline(data, 100);
    // 向文件写入用户输入的数据
    outfile << data << endl;

    cout << "Enter your age: ";
    cin >> data;
    cin.ignore();

    // 再次向文件写入用户输入的数据
    outfile << data << endl;

    // 关闭打开的文件
    outfile.close();

    // 以读模式打开文件

    ifstream infile;
    infile.open("afile.dat");

    cout << "Reading from the file" << endl;

    infile >> data;

    // 在屏幕上写入数据
    cout << data << endl;

    // 再次从文件读取数据,并显示它
    infile >> data;
    cout << data << endl;

    // 关闭打开的文件
    infile.close();

    return 0;
}

二进制文件输入输出

#include <iostream>
#include <fstream>
using namespace std;
class Date {
        int month, day, year;
    public:
        Date( int m=1, int d=1, int y=1901) {
            month = m;
            day = d;
            year = y;
        }
        friend ostream &operator<< ( ostream & os, Date dt) {
            os << dt.month << '/' << dt.day << '/'<< dt.year<<'\n';
            return os;
        }
};

int main() {
    Date dt1,dt2( 2, 2, 2002);
    cout << "dt1 is: "<<dt1<<"dt2 is: "<<dt2<<endl;


    ofstream fout("date.dat",ios::binary);
    fout.write((char*)&dt1,sizeof(dt1));
    fout.write((char*)&dt2,sizeof(dt2));

    fout.close( );


    ifstream fin("date.dat",ios::binary);
    fin.read((char*)&dt2,sizeof(dt2));//第一次读入的数据块存放到dt2中
    fin.read((char*)&dt1,sizeof(dt1));
    cout << "dt1 is: "<<dt1<<"dt2 is: "<<dt2;
    fin.close( );

    return 0;
}

异常处理

基本思想

函数 A 在执行过程中发现异常时可以不加处理,而只是“拋出一个异常”给 A 的调用者,假定为函数 B。

拋出异常而不加处理会导致函数 A 立即中止,在这种情况下,函数 B 可以选择捕获 A 拋出的异常进行处理,也可以选择置之不理。如果置之不理,这个异常就会被拋给 B 的调用者,以此类推。

如果一层层的函数都不处理异常,异常最终会被拋给最外层的 main 函数。main 函数应该处理异常。如果main函数也不处理异常,那么程序就会立即异常地中止。

C++ 通过 throw 语句和 try…catch 语句实现对异常的处理。

throw  表达式
该语句拋出一个异常。异常是一个表达式,其值的类型可以是基本类型,也可以是类。

try…catch 语句的语法如下:

try {
    语句组
}
catch(异常类型) {
    异常处理代码
}
...
catch(异常类型) {
    异常处理代码
}

try…catch 语句的执行过程是:
执行 try 块中的语句,如果执行的过程中没有异常拋出,那么执行完后就执行最后一个 catch 块后面的语句,所有 catch 块中的语句都不会被执行;
如果 try 块执行的过程中拋出了异常,那么拋出异常后立即跳转到第一个“异常类型”和拋出的异常类型匹配的 catch 块中执行(称作异常被该 catch 块“捕获”),执行完后再跳转到最后一个 catch 块后面继续执行。

判断题

If you are not interested in the contents of an exception object, the catch block parameter may be omitted.                                                                                 T

(如果您对异常对象的内容不感兴趣,则可以省略catch块参数)

catch (type p) acts very much like a parameter in a function. Once the exception is caught, you can access the thrown value from this parameter in the body of a catch block.             T

(catch(p类型)的作用非常像函数中的一个参数。一旦捕捉到异常,就可以访问catch块主体中该参数的抛出值)

选择题

One of the major features in C++ is ( ) handling,which is a better way of handling errors.
A.data
B.pointer
C.test
D.exception

What is wrong in the following code?

 vector<int> v;
 v[0] = 2.5;

A.The program has a compile error because there are no elements in the vector.
B.The program has a compile error because you cannot assign a double value to v[0].
C.The program has a runtime error because there are no elements in the vector.
D.The program has a runtime error because you cannot assign a double value to v[0].

A.程序出现编译错误,因为vector中没有元素

B.程序出现编译出错,因为无法为v[0]分配双值

C.程序出现运行时错误,因为vector中没有元素

D.程序出现运行时间错误,因为无法给v[0]分配双值。

vector赋值 push_back(element)方法向vector末尾添加元素

If you enter 1 0, what is the output of the following code?

#include "iostream"
using namespace std;

int main()

{
  // Read two integers

    cout << "Enter two integers: ";

  int number1, number2;

  cin >> number1 >> number2;

  try
  {
    if (number2 == 0)
      throw number1;

    cout << number1 << " / " << number2 << " is "
      << (number1 / number2) << endl;

    cout << "C" << endl;
  }
  catch (int e)
  {
    cout << "A" ;
  }

  cout << "B" << endl;

  return 0;
}

A.A
B.B
C.C
D.AB

The function what() is defined in ______________.
A.exception
B.runtime_error
C.overflow_error
D.bad_exception

下列关于异常的描述中,错误的是()。
A.编译错属于异常,可以抛出
B.运行错属于异常
C.硬件故障也可当异常抛出
D.只要是编程者认为是异常的都可当异常抛出

下列关于异常类的说法中,错误的是。
A.异常类由标准库提供,不可以自定义
B.C++的异常处理机制具有为抛出异常前构造的所有局部对象自动调用析构函数的能力
C.若catch块采用异常类对象接收异常信息,则在抛出异常时将通过拷贝构造函数进行对象复制,异常处理完后才将两个异常对象进行析构,释放资源
D.异常类对象抛出后,catch块会用类对象引用接收它以便执行相应的处理动作

下列关于异常处理的说法不正确的是( )。
A.异常处理的throw与catch通常不在同一个函数中,实现异常检测与异常处理的分离。
B.catch语句块必须跟在try语句块的后面,一个try语句块后可以有多个catch语句块。
C.在对函数进行异常规范声明时,若形参表后没有任何表示抛出异常类型的说明,它表示该函数不能抛出任何异常。
D.catch语句块中,catch(…)表示该catch可以捕捉任意类型的异常,必须将catch(…)放在catch结构的最后。

C++处理异常的机制是由()3部分组成。
A.编辑、编译和运行
B.检查、抛出和捕获
C.编辑、编译和捕获
D.检查、抛出和运行

Suppose Exception2 is derived from Exception1. Analyze the following code.

try {
    statement1;
    statement2;
    statement3;
}

catch (Exception1 ex1)
{
}

catch (Exception2 ex2)
{
}

A.If an exception of the Exeception2 type occurs, this exception is caught by the first catch block.
B.If an exception of the Exeception2 type occurs, this exception is caught by the second catch block.
C.The program has a compile error because these two catch blocks are in wrong order.
D.The program has a runtime error because these two catch blocks are in wrong order.

注:Exception2派生自Exception1

Suppose that statement2 throws an exception of type Exception2 in the following statement:

try {

    statement1;
    statement2;
    statement3;
}
catch (Exception1 ex1)
{
}
catch (Exception2 ex2)
{
}
catch (Exception3 ex3)
{
    statement4;
    throw;
}
statement5;

A.statement2

B.statement3

C.statement4

D.statement5

执行的过程中没有异常拋出,那么执行完后就执行最后一个 catch 块后面的语句,所有 catch 块中的语句都不会被执行

Suppose that statement3 throws an exception of type Exception3 in the following statement:

try {

statement1;
statement2;
statement3;
}
catch (Exception1 ex1)
{
}
catch (Exception2 ex2)
{
}
catch (Exception3 ex3)
{
statement4;
throw;
}

statement5;

Which statements are executed after statement3 is executed?
A.statement2
B.statement3
C.statement4
D.statement5

try 块执行的过程中拋出了异常,那么拋出异常后立即跳转到第一个“异常类型”和拋出的异常类型匹配的 catch 块中执行(称作异常被该 catch 块“捕获”),执行完后再跳转到最后一个 catch 块后面继续执行。

函数题

6-1 除数为零异常

下面是这个程序处理除数为零的异常,在函数 division( )中抛出一个除以零的异常,并在main函数中的 catch 块中捕获该异常。

函数接口定义:

double division(int a, int b);
其中a为被除数,b为除数。

裁判测试程序样例:

#include <iostream>
using namespace std;

/* 请在这里填写答案 */

int main ()
{
    int x,y;
    double z = 0;

    cin>>x>>y;
    try {
        z = division(x, y);
        cout << z << endl;
    }catch (const char* msg) {
        cout << msg << endl;
    }
    return 0;
}

输入样例:

在这里给出一组输入。例如:

2  0

输出样例:

在这里给出相应的输出。例如:

Divided by zero!
double division(int a, int b)
{
  if (b == 0)
    throw ("Divided by zero!");
  return a/b;
}

编程题

7-1 求平方根函数mySqrt的异常处理

改造下面的求平方根函数mySqrt,当x小于0时,输出错误信息:"Error: Can not take sqrt of negative number";当x不小于0时,输出x的平方根。要求在main函数中采用C++的异常处理方法。

double mySqrt(double x)
{
    return sqrt(x);
}

输入格式:

4

输出格式:

The sqrt of 4 is 2

输入样例:

-9

输出样例:

Error: Can not take sqrt of negative number
#include <bits/stdc++.h>

using namespace std;

double mySqrt(double x)
{
    if (x <= 0) throw ("Error: Can not take sqrt of negative number");
    return sqrt(x);
}

int main()
{
    double n, r;
    cin >> n;
    try
    {
        r = mySqrt(n);
        cout << "The sqrt of " << n << " is " << r;
    }
    catch(const char *a)
    {
        cout << a;
    }
    return 0;
}

复制构造函数

复制构造函数(Copy constructor)是c++中的一个特殊构造函数,也称拷贝构造函数,它只有一个参数,参数类型为同类对象的引用。

如果没有定义复制构造函数,那么编译器将生成默认的复制构造函数。默认的复制构造函数完成复制的功能。

复制构造函数的参数为同类对象的引用,可以是常应用,也可以是非常引用。形如类名::类名(类名&)或类名::类名(const 类名&)

复制构造函数的参数一定要是同类对象的引用

判断题

将构造函数说明为纯虚函数是没有意义的。                  T

对象间赋值将调用拷贝构造函数。                                  F 

选择题

假设MyClass是一个类,则该类的拷贝初始化构造函数的声明语句为( )
A.MyClass&(MyClass x);
B.MyClass(MyClass x);
C.MyClass(MyClass &x);
D.MyClass(MyClass *x);

C++提供的可有效分配对象空间的运算符是( )
A.delete
B.new
C.pos
D.auto

在以下哪种情形,复制构造函数会被调用。
A.当一个对象采用引用方式,作为参数传递给一个函数
B.当一个函数采用值方式,返回一个对象
C.当一个对象赋值给另一个对象
D.以上答案都不对

假设A是一个类的名字,下面哪段程序不会用到A的拷贝构造函数?
A.A a1,a2; a1=a2;
B.void func( A a) { cout<<"good"<< endl; }
C.A func() { A tmp; return tmp;}
D.A a1; A a2(a1);

函数题

6-1 学生成绩的快速录入(构造函数)

现在需要录入一批学生的成绩(学号,成绩)。其中学号是正整数,并且录入时,后录入学生的学号会比前面的学号大;成绩分两等,通过(Pass,录入时用1代表),不通过(Fail,录入时用0代表)。

由于很多学号都是相邻的,并且学号相邻的学生成绩常常相同。所以在录入时,适当地加了速。如果当前学生的学号比前面的学号大1,且成绩与前面的成绩相同,则只输入0即可。

类定义:

完成Student类

裁判测试程序样例:

#include<iostream>
using namespace std;

/* 请在这里填写答案 */

int main(){
    const int size=100;
    int i, N, no, score;
    Student *st[size];
    cin>>N;
    for(i=0; i<N; i++){
        cin>>no;
        if(no>0){
            cin>>score;
            st[i]=new Student(no, score);
        }
        else
            st[i]=new Student(*st[i-1]);
    }
    cout<<Student::count<<" Students"<<endl;
    for(i=0;i<N;i++) st[i]->display();
    for(i=0;i<N;i++) delete st[i];
    return 0;
}

输入样例:

5
3 0
0
7 1
0
12 1

输出样例:

5 Students
3 Fail
4 Fail
7 Pass
8 Pass
12 Pass
class Student {
	private:
		int no,score;
	public:
		static int count;	//静态成员变量 
		Student(int n,int s) {
			no=n;
			score=s;
			count++;
		}
		Student(Student &s) {
			no=s.no+1;
			score=s.score;
			count++;
		}
		void display() {
			cout<<no<<" ";
			if(score)
				cout<<"Pass"<<endl;
			else
				cout<<"Fail"<<endl;
		}
};
int Student::count=0;

6-3 为my_string类创建复制构造函数copy constructor

为下面的my_string类创建一个复制构造函数,并将定义该类的代码提交。

my_string类的定义:

class my_string {
   char *s;
public:
   my_string(char *str)  {
      s = new char[strlen(str)+1];
      strcpy(s, str);
   }
   ~my_string() {
       if(s) delete [] s;
       cout << "Freeing s\n"; 
    }
   void show() { cout << s << "\n"; }
};

裁判测试程序样例:

#include <iostream>
#include <cstring>
using namespace std;
// 你提交的代码将被嵌入到这里


int main()
{
   char str[80];
   cin>>str;
   my_string obj(str); 

   my_string ob1(obj);    
   my_string ob2=ob1;    //这里也调用复制构造函数
  
   ob1.show();
   ob2.show();

   return 0;
}

输入样例:

ByeBye

输出样例:

ByeBye
ByeBye
Freeing s
Freeing s
Freeing s
class my_string {
   char *s;
public:
   my_string(char *str)  {
      s = new char[strlen(str)+1];
      strcpy(s, str);
   }
    my_string(const my_string &str){
         s = new char[strlen(str.s)+1];
        strcpy(s, str.s);
    }
   ~my_string() {
       if(s) delete [] s;
       cout << "Freeing s\n"; 
    }
   void show() { cout << s << "\n"; }
};

;