C++复习课
试卷说明:
题型: 填空 + 编程题目
填空题
-
构造函数无返回类型,与类名同名;
复制构造函数用于创建对象,形实参结合,返回和接收对象。
补充:
只有在声明语句中使用一个变量初始化另一个对象时才调用复制构造函数!
案例如下:int main(void){ Point p1(1,2),p2 = p1; return 0; }
反之,则不调用复制构造函数int main(void){ Point p1(1,2),p2; p2(p1); }
-
类的继承包含
public
,private
和protect
方式。
他们的区别是:类成员的访问控制方式不同
详细差异:public
:任何外部函数都可以访问公有类型的数据和成员private
: 只允许本类中的函数访问,类外部的任何函数都不能访问protected
: 与private类似,具体表现在继承和派生时对派生类的影响不同
-
友元类和友元函数的定义
友元函数定义如下:friend 类型 函数名(形式参数);
友元类定义如下:
friend class 类名; //类名必须是已在程序中定义过的类
-
类的静态成员变量: 统计类对象的个数
类的静态成员函数: 调用静态成员变量 (补充:更完整说法 调用静态成员变量的值并返回)补充知识:
类的静态成员的提出是为了解决同类对象之间的数据共享问题
思考一下:在统计班级人数总数的case中为什么不可以定义全局变量呢?
显然,类设计的初衷是为了实现对数据的封装和隐藏,因此若采用全局变量,则无法实现对数据成员的隐藏。因此我们设计了静态成员变量!
静态成员变量的定义如下:static 数据类型 变量名称;
注意: 静态成员不可以在类中进行赋初值!
可以通过下面这个简化case来粗略学习一下:#include <iostream> using namespace std; class StuNum{ private: ... static int num; //注意:此处禁止进行赋初值操作 public: ... } int StuNum :: num = 0; //此处进行赋初值,并且赋值格式如左 int main(void) { ... return 0; }
-
纯虚函数
void f() = 0
构成 抽象类;
抽象类 可以派生子类,但是不能够创建对象,可以声明类变量。
补充:
纯虚函数定义:
一个虚函数 -> 一个在基类中声明的虚函数 -> 特点:不定义函数体,由其所有派生类根据具体情况来对各自的函数进行实现!
抽象类因为不可以实例化,因此也不可以作为函数的返回值。
如果派生类只实现抽象类中的部分纯虚函数,那么它依旧是抽象类;只有当所有纯虚函数均被实现后,其才不是抽象类! -
构造函数的默认形参值
定义时:从右向左定义;
调用时:从左向右匹配; -
函数重载的条件有 形参个数 和 类型 以及 const等。
-
动态联编和静态联编:
动态联编是在运行时确定所调用的函数名与操作函数的绑定;
静态联编是在编译时确定所调用的函数名与操作函数的绑定; -
虚函数:基类中说明了虚函数后,派生类中与其对应的函数可以不必说明为虚函数;
补充:
虚函数定义:
一个非静态成员函数 -> 一个使用保留字virtual
声明的非静态成员函数
定义如下:virtual 函数返回类型 函数名(<形参列表>);
虚函数是实现动态联编的基础!应满足如下三个规则:
(1) 具有符合类型兼容规则的公有派生类层次结构。
(2) 在派生类中重新定义基类中的虚函数,对其进行覆盖。
(3) 通过基类指针或者基类引用访问虚函数。(较为关键且容易疏忽) -
友元机制:破坏类的封装与隐藏,引入友元机制的主要作用?
作用: 友元提供了在不同类的成员函数之间,类的成员函数与一般函数之间进行数据共享的机制。即可以实现在类的外部访问类的私有成员。
补充:
可以理解成友元为封装隐藏这堵不透明的墙开了一个小孔,外界可以通过这个小孔窥视内部的秘密。
友元的正确使用可以提高程序的运行效率,但是破坏了类的封装性和数据的隐蔽性,导致程序的可维护性变差,因此我们应谨慎使用。
作为一种编程技术手段,友元为我们程序员提供了一种面向对象程序和面向过程程序的相互衔接的接口。从根本上说面向对象的分析与设计方法并不能彻底解决现实生活中的一切需求。许多按照对象化设计的软件系统往往保留一些供早起程序访问的接口,来扩大自身的功能,提高自身的产品竞争能力。
友元较为实际的应用在于运算符重载,提高了软件系统的灵活性。 -
C++语言中不能重载的运算符有?
有5种 如下:(1)
.
(成员访问运算符)
(2).*
(成员指针访问运算符)
(3)::
(作用域分辨符)
(4)sizeof
(计算数据大小运算符)
(5)? :
(三目运算符)
补充:
.
和.*
提供访问成员的基本功能,禁止重载可以避免混乱!
::
和sizeof
的操作对象是类型,而不是表达式。
三目运算符
本质上是if语句
,不值得重载 -
C++语言中的多态主要包括静态多态和动态多态
补充:
常见编译时多态有 函数重载,运算符重载,函数模板
运行时多态需要使用虚函数来实现 -
在重载一个运算符时,如果其参数表中有一个参数,则说明运算符中有几个参数?
两个
补充:
(1) 双目运算符重载为类的成员函数时,函数只显式说明了一个参数,该形参是运算符的右操作数;
(2) 前置单目运算符重载为类的成员函数时,不需要显示说明参数,即函数没有形参;
(3) 后置单目运算符重载为类的成员函数时,函数要带有一个整形形参
编程题
题目1:
成员变量:HMS;
构造函数,settime,showtime,计时++,创建对象,调用函数。
定义一个描述时钟的类MyClock,数据成员包括时钟的时、分、秒(int hour;int minute;int second;),给该时钟类增加合适的成员函数,使得下面的main函数的运行结果:
void main(){
MyClock myClock(23,59,59);
myClock.ShowTime();
(myClock++).ShowTime();
(++myClock).ShowTime();
}
代码如下:
/*
Main函数操作:
1. 赋初值
2.Showtime
3. ++重载
*/
#include <iostream>
using namespace std;
class MyClock{
private:
int H,M,S; //hour min sec
public:
MyClock(int h,int m,int s):H(h),M(m),S(s){
//已由初始化列表完成赋值
}
void SetTime(int h,int m,int s){
H = h;
M = m;
S = s;
}
void ShowTime(){
printf("%02d:%02d:%02d\n",H,M,S);
}
MyClock operator ++(); //前置++参数表为空
MyClock operator ++(int); //后置++参数表为int 实际上是哑参数 没有实际意义
};
/*
函数:重载前置++
需要注意进位清零的问题
*/
MyClock MyClock::operator ++(){
this -> S++;
if(S==60){
this -> M++;
S = 0; //进位清零操作
}
if(M==60){
this -> H++;
M = 0;
}
if(H==24){
H = 0;
}
return *this;
}
/*
函数:重载后置++运算符
后置++ 返回的是原来的值 因此需要定义一个变量来保存原来的类对象
故定义old
*/
MyClock MyClock::operator ++(int){
MyClock old = *this;
++(*this);
//此处应注意:++必须为前置 因为我们上面已经重载过前置++ 故本处可以直接使用即可
return old;
}
int main(){
MyClock myClock(23,59,59);
myClock.ShowTime();
(myClock++).ShowTime();
(++myClock).ShowTime();
}
本题目知识补充:
1.运算符重载模板
(1) 重载为类的非成员函数(即友元函数)
//类中声明
friend 返回类型 operator 运算符(<参数表>);
//类外定义
返回类型 operator 运算符(<参数表>);
(2) 重载为类的成员函数
//类中声明
返回类型 operator 运算符(<参数表>);
//类外声明
返回类型 类名::operator 运算符(<参数表>);
以上定义模板的差异其实不难理解,可以参照友元函数和类成员函数的差异即可!
友元函数是类外部函数,自然不需要类名以及作用域分辨符
反之,类内成员函数需要其来加以分辨!
2.this指针
可以试着理解下面这句话:
this指针总是指向其所在成员函数的所在类的对象!
当我们使用this指针时必然是在成员函数中使用,而成员函数必定有其自己的类,而类必须要有对象才能够进行调用成员函数!
这样我相信就可以很容易理解this指针了吧!
题目二
学生类:
成员变量:学号,姓名,学生个数统计count静态变量;
函数:构造函数,复制构造函数,统计count的静态函数;创建对象,调用函数。
#include <iostream>
using namespace std;
class Student{
private:
string ID;
string Name;
static int count;//切记不可以在这里赋初值
public:
Student(){
cout<<"Student构造函数调用完毕!"<<endl;
}
Student(string id,string name):ID(id),Name(name){
count++;
}
Student(Student &temp){
ID = temp.ID;
Name = temp.Name;
count++;
}
~Student(){
//cout<<"Student析构函数调用完毕!"<<endl;
count--;
}
void show(){
cout<<Name<<" "<<ID<<endl;
}
static void getCount(){
cout<<"count = "<<count<<endl;
}
};
int Student::count = 0;
int main(void){
Student::getCount();
Student p1("01","czy"),p2(p1);
p1.show();
Student::getCount();
Student p3("02","qwj");
p3.show();
Student::getCount();
return 0;
}
题目三
学生类:
成员变量:学号,姓名char[],年龄,计算机、网络等多门课成绩
成员函数:构造函数,复制构造函数,使用友元函数统计3个学生的均分。
#include <iostream>
#include <string.h>
using namespace std;
class Student{
private:
string ID;
char * Name;
int Age;
double CS;
double Internet;
public:
Student(string id,char *name,int age,int cs,int internet){
ID = id;
Name = name;
Age = age;
CS = cs;
Internet = internet;
}
Student(Student &temp){
ID = temp.ID;
Name = temp.Name;
Age = temp.Age;
CS = temp.CS;
Internet = temp.Internet;
}
~Student(){
}
friend void StuAver_Grade(Student N1,Student N2,Student N3);
};
void StuAver_Grade(Student N1,Student N2,Student N3){
int Aver_CS = (N1.CS+N2.CS+N3.CS)/3.0;
int Aver_Internet = (N1.Internet+N2.Internet+N3.Internet)/3.0;
cout<<"计算机平均分:"<<Aver_CS<<endl;
cout<<"网络平均分:"<<Aver_Internet<<endl;
cout<<"总成绩平均分:"<<(Aver_CS+Aver_Internet)/2.0<<endl;
}
int main(void){
Student p1("01","czy",19,99,100),p2(p1);
Student p3("03","qwj",20,95,96);
StuAver_Grade(p1,p2,p3);
return 0;
}
题目四:
Father类派生son类:
成员函数:构造函数和复制构造函数和析构函数并输出字符串,运行测试。
补充:派生类的构造函数声明一般语法
<派生类名>::<派生类名>(参数总表):基类名1(参数表1),...,基类名m(参数表m),内嵌对象名1(内嵌对象参数表1),...,内嵌对象名2(内嵌对象参数表2)
{
//派生类新增普通成员的初始化语句
}
代码如下:
#include <iostream>
using namespace std;
class Father{
private:
string Name_1;
public:
Father(){
cout<<Name_1<<"类的缺省样式构造函数调用完毕!"<<endl;
}
Father(string name){
Name_1 = name;
cout<<Name_1<<"类带参构造函数调用完毕!" <<endl;
}
Father(Father &temp){
Name_1 = temp.Name_1;
cout<<Name_1<<"类拷贝构造函数调用完毕!" <<endl;
}
~Father(){
cout<<Name_1<<"类析构函数调用完毕!"<<endl;
}
};
class son:public Father{
private:
string Name_2;
Father father;
public:
son(){
cout<<this<<"Son类的缺省样式构造函数调用完毕!"<<endl;
}
son(Father temp,string name1,string name2):father(temp),Father(name1){
Name_2 = name2;
cout<<"Son类的带参构造函数调用完毕!"<<endl;
}
son(son &temp){
Name_2 = temp.Name_2;
}
~son(){
cout<<"Son类的析构函数调用完毕!"<<endl;
}
};
int main(void){
Father czy("Father");
son qwj(czy,"Father","Son");
return 0;
}
注:自认为本题写的不是很好,疑点很多!后期有时间后会进行修改!
题目5:
定义一个点类与矩形类(组合类),其中矩形类的成员Point p0表示其左上角坐标,p1是右下角坐标,注意构造函数!
代码如下:
#include <iostream>
using namespace std;
class Point{
private:
int x,y;
public:
Point(int X,int Y):x(X),y(Y){
}
Point(Point &temp){
x = temp.x;
y = temp.y;
}
~Point(){
}
int getX(){
return x;
}
int getY(){
return y;
}
void show(){
cout<<x<<','<<y<<endl;
}
};
class Rectangle{
private:
int Length,Wide;
Point p0,p1;
public:
Rectangle(Point P0,Point P1):p0(P0),p1(P1){
Length = abs(p1.getX()-p0.getX());
Wide = abs(p1.getY()-p0.getY());
}
~Rectangle(){
}
void show(){
cout<<"矩阵的长:"<<Length<<"矩阵的宽:"<<Wide<<endl;
cout<<"矩阵面积:"<<Length*Wide<<endl;
}
};
int main(void){
Point p0(1,1),p1(2,2);
Rectangle rec(p0,p1);
rec.show();
return 0;
}
题目六
编写-一个抽象类Shape, 在此基础之上派生出类Rectangle(矩形类)和Circle(圆类), 二者都有计算面积的函数getArea( )和周长的函数getPerim( )。
#include <iostream>
#define PI 3.14
using namespace std;
class Shape{
public:
Shape(){
//构造函数
}
~Shape(){
//析构函数
}
virtual double getArea() = 0; //纯虚函数定义
virtual double getPerim() = 0; //纯虚函数定义
};
class Circle:public Shape{
private:
int r;
public:
Circle(int R){
r = R;
}
~Circle(){
}
double getArea(){
return PI*r*r;
}
double getPerim(){
return 2*PI*r;
}
};
class Rectangle:public Shape{
private:
int x,y;
public:
Rectangle(int X,int Y){
x = X;
y = Y;
}
double getArea(){
return x*y;
}
double getPerim(){
return 2*(x+y);
}
};
int main(void){
Circle c(3);
Rectangle rec(4,5);
cout<<c.getArea()<<endl;
cout<<c.getPerim()<<endl;
cout<<rec.getArea()<<endl;
cout<<rec.getPerim()<<endl;
return 0;
}
题目7
分别定义一组重载函数,计算两个整数、浮点数(float或double)、 复数之和。
#include <iostream>
using namespace std;
//运算符重载->用于计算复数做准备
class Complex{
private:
double real,imag;
public:
Complex(double r,double i){
real = r;
imag = i;
}
Complex operator + (const Complex &c){
Complex t = *this;
t.real += c.real;
t.imag += c.imag;
return t;
}
void show(){
cout<<real<<"i+"<<imag<<'j'<<endl;
}
};
//函数重载环节
int sum(int a,int b){
return a+b;
}
float sum(float a,float b){
return a+b;
}
double sum(double a,double b){
return a+b;
}
void sum(Complex c1,Complex c2){
Complex c = c1 + c2;
c.show();
}
int main(void){
cout<<sum(1,2)<<endl;
cout<<sum(1.2,2.4)<<endl<<endl;
cout<<sum(1.111111,1.111111)<<endl;
Complex c1(1,2),c2(1,2);
sum(c1,c2);
return 0;
}
问题8
港口集团对员工月工资:工资+奖金+小时工资*工作量。
处长工资+奖金=10K+20K;科长工资+奖金=5k+5K;科员工资+奖金=5k+1k。
定义员工抽象类,派生其他类,编写程序求收入。
virtual double 计算工资()=0;纯虚函数姓名[20];specialty[20];//专业技能;年龄;工资;小时工资;工作量;职称;奖金;
构造函数、计算工资函数。main测试。
/*
工资 = 基本工资(wage_base) + bonus + wage_hourly*work_time;
*/
/*注释:
科长: kz_chief
处长: cz_chief
科员: ky_chief
*/
#include <iostream>
using namespace std;
/*
问题复盘:
起初打算将人员的工资信息和奖金信息放在相应的类中,
但是因为访问权限的问题导致无法访问时薪和工作时间...OK貌似解决了
只需要再申请一个函数空间去计算时薪即可
问题留待:
其实很想全部都换成public继承,但是基于这是一种带代码陋习...算了
*/
class Employee{
private:
string name;
int age;
int wage_hourly;
int work_time;
public:
Employee(string Name,int Age,int Wage_hourly,int Work_time){
name = Name;
age = Age;
wage_hourly = Wage_hourly;
work_time = Work_time;
}
virtual double wage_operate() = 0;
double wage_time(){
return wage_hourly*work_time;
}
};
class kz_chief:public Employee{
private:
int kz_wage = 10000;
int kz_bonus = 20000;
public:
kz_chief(string Name,int Age,int Wage_hourly,int Work_time):Employee(Name,Age,Wage_hourly,Work_time){
}
double wage_operate(){
return kz_wage + kz_bonus + wage_time();
}
};
class cz_chief:public Employee{
private:
int cz_wage = 5000;
int cz_bonus = 5000;
public:
cz_chief(string Name,int Age,int Wage_hourly,int Work_time):Employee(Name,Age,Wage_hourly,Work_time){
}
double wage_operate(){
return cz_wage + cz_bonus + wage_time();
}
};
class ky_chief:public Employee{
private:
int ky_wage = 5000;
int ky_bonus = 1000;
public:
ky_chief(string Name,int Age,int Wage_hourly,int Work_time):Employee(Name,Age,Wage_hourly,Work_time){
}
double wage_operate(){
return ky_wage + ky_bonus + wage_time();
}
};
int main(void)
{
//读入变量依次为 姓名 年龄 时薪 工作时长
kz_chief kz("czy",18,50,8);
cz_chief cz("qwj",17,40,9);
ky_chief ky("yll",19,20,10);
cout<<kz.wage_operate()<<endl;
cout<<cz.wage_operate()<<endl;
cout<<ky.wage_operate()<<endl;
return 0;
}
问题九:
虚基类:A,A派生B1、和B2,B1和B2派生出C
A:ID、name;构造函数等;B1和B2:职称,部门、构造函数等;C:职务级,专业技能、构造函数等;
继承关系如下图所示:
/*
本题目是一个典型的虚继承问题,即继承构成菱形
*/
#include <iostream>
#include <string.h>
using namespace std;
class A{
private:
string ID,Name;
public:
A(string id,string name){
ID = id;
Name = name;
}
};
class B1:virtual public A{
private:
string title; //职称
string department; //部门
public:
B1(string id,string name,string Title,string Department):A(id,name),title(Title),department(Department){
}
~B1(){
}
};
//与B类雷同
class B2:virtual public A{
private:
string title; //职称
string department; //部门
public:
B2(string id,string name,string Title,string Department):A(id,name),title(Title),department(Department){
}
~B2(){
}
};
class C:public B1,public B2{
private:
string Grade; //职务等级
string Skill;
public:
C(string id,string name,string Title1,string Department1,string Title2,string Department2,string grade,string skill):A(id,name),B1(id,name,Title1,Department1),B2(id,name,Title2,Department2){
Grade = grade;
Skill = skill;
}
~C(){
}
};
int main(void){
//...
//电脑没电了 不再测试了
return 0;
}
补充:
虚基类是什么呢?
实际上,我们看继承图,C继承B1,B2可以很容易看到其继承了两次A
某种意义上这会导致内存的浪费
因此我们提出了虚基类的概念来解决这个问题
问题十:
某小型公司,主要有三类员工(Employee):
行政人员(AdminStaff)、兼职销售员(Salesman)、经理(Manager)。
行政人员拿固定月薪5000元(静态浮点型常量FS),兼职销售员按该销售员当月销售额的4%提成;
经理既是行政人员,也做销售工作,因此其月薪是固定月薪+销售提成。
运用虚基类+虚函数相关知识,设计抽象类Employee,包含姓名name数据成员(允许用string类)和计算月薪函数float pay();
然后派生出AdminStaff,增加固定月薪fixedSalary数据成员,实现其pay函数;
派生出Salesman,增加当月销售额sales数据成员,实现其pay函数;
再共同派生出Manager,实现其pay函数。在main中为每个派生类各创建一个对象;
放入Employee类型的指针数组;在for循环中统一处理不同派生类的对象,依次显示每位员工的姓名和工资,并汇总工资总额。
#include <iostream>
using namespace std;
class Employee{
private:
string Name;
public:
Employee(string name){
Name = name;
}
string getName(){
return Name;
}
virtual float pay() = 0;
};
class AdminStaff:virtual public Employee{
private:
float FixedSales = 5000;
public:
AdminStaff(string name):Employee(name){
}
float pay(){
return FixedSales;
}
};
class Salesman:virtual public Employee{
private:
float Sales;
public:
Salesman(string name,float sales):Employee(name){
Sales = sales;
}
float pay(){
return Sales*0.04; //销售员工资为销售额的0.04
}
};
class Manage:virtual public Employee{
private:
float FixedSales = 5000;
float Sales;
public:
Manage(string name,float sales):Employee(name),Sales(sales){
//
}
float pay(){
return FixedSales+Sales*0.04; //经理工资 = 固定工资 + 销售额*0.04
}
};
int main(void)
{
AdminStaff admin("qwj");
Salesman salesman("yll",50000);
Manage manage("czy",50000);
Employee *czy[3] = {&admin,&salesman,&manage};//指针数组
for(int i=0;i<3;i++){
cout<<(*czy[i]).getName()<<"的工资为:"<<(*czy[i]).pay()<<endl;
}
return 0;
}
运行结果如下: