c/c++零基础坐牢第十六天

 

c/c++从入门到入土(16)

开始时间2023-05-24 20:18:03

结束时间2023-05-25 11:41:34

前言:目前已完成基础内容及类与对象相关知识的复习,今日复习第五章 数据的共享与保护,第七章 类的继承,并对作业六 继承及实验二 类的继承与派生进行讨论。课本知识参考于清华大学出版社郑莉老师的《C++语言程序设计》第五版,以下为自制思维导图:

 第五章 数据的共享与保护

复制代码
 1 作用域是一个标识符在程序正文中有效的区域。
 2 c艹语言中标识符的作用域与函数原型作用域、局部作用域(块作用域)、类作用域、文件作用域、命名空间作用域和限定作用域的enum枚举类。
 3 在函数原型声明时形式参数的作用范围就是函数原型作用域。
 4 函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。
 5 函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的花括号为止。
 6 具有局部作用域的变量也称为局部变量。
 7 类可以被看成是一组有名成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。
 8 通过表达式x.m或者X::m。这正是程序中访问对象成员的最基本方法。
 9 通过ptr->m这样的表达式,其中ptr为指向X类的一个对象的指针。
10 不在前述各个作用域中出现的声明,就具有文件作用域,这样声明的标识符其作用域开始于声明点,结束于文件尾。
11 具有文件作用域的变量也称为全局变量。
12 程序运行到某一点,能够引用到的标识符,就是该处可见的标识符。
13 对象(包括简单变量)都有诞生和消失的时刻。对象从诞生到结束的这段时间就是它的生存期。在生存期内,对象将保持它状态(即数据成员的值),变量也将保持它的值不变,直到它们被更新为止。
14 如果对象的生存期与程序的运行期相同,我们称它具有静态生存期。
15 如果要在函数内部的局部作用域中声明具有静态生存期的对象,则要使用关键字static。
16 局部作用域中静态变量的特定是,它并不会随着每次函数调用而产生一个副本,也不会随着函数返回而失效,也就是说,当一个函数返回后,下一次再调用时,该变量还会保持上一回的值,即使发生了递归调用,也不会为该变量建立新的副本,该变量会在各次调用间共享。
17 定义时未指定初值的基本类型静态生存期变量,会被以0值初始化,而对于动态生存期变量,不指定初值意味着初值不确定。
18 局部生存期对象诞生于声明点,结束于声明所在的块执行完毕之时。
19 类的成员对象也有各自的生存期。不用static修饰的成员对象,其生存期都与它们所属对象的生存期保持一致。
标识符的作用域与可见性&对象生存期
复制代码
复制代码
 1 在结构化程序设计中程序模块的基本单位是函数,因此模块间对内存中数据的共享是通过函数与函数之间的数据共享来实现的,其中包括两个途径——参数传递和全局变量。
 2 类中的数据成员可以被同一类中的任何一个函数访问。
 3 一方面在类内部的函数之间实现了数据的共享;
 4 另一方面这种共享是受限制的,可以访问设置适当的访问控制属性,把共享只限制在类的范围之内,对类外来说,类的数据成员仍是隐藏的,达到了共享与隐藏双全。
 5 静态成员是解决同一个类的不同对象之间数据和函数共享问题的。
 6 我们说“一个类的所有对象具有相同属性”,是指属性的个数、名称、数据类型相同,各个对象的属性值则可以各不相同,这样的属性在面向对象方法中称为“实例属性”,在c艹中以类的非静态数据成员表示。
 7 类属性是描述类的所有对象共同特征的一个数据项,对于任何对象实例,它的属性值是相同的。
 8 静态数据成员具有静态生存期。一般用法是“类名::标识符”。
 9 C++11标准支持常量表达式类型修饰(constexpr或const)的静态常量在类内初始化,此时仍可在类外定义该静态成员,但不能做再次初始化操作。
10 在对类的静态数据成员初始化的同时,还可以引用类的其他私有成员。例如,如果一个类T存在类型为T的静态私有对象,那么可以引用该类的私有构造函数将其初始化。
11 所谓静态成员函数结束使用static关键字声明的函数成员,同静态数据成员一样静态成员函数也属于整个类,由同一个类的所有对象共同拥有,为这些对象所共享。
12 静态成员函数可以通过类名或对象名来调用,而非静态成员函数只能通过对象名来调用。
13 虽然静态成员函数可以通过类名和对象名两种方式调用,但一般习惯于通过类名调用。因为即使通过对象名调用,起作用的也只是对象的类型信息,与所使用的具体对象毫无关系。
14 静态成员函数可以直接访问该类的静态数据和函数成员。而访问非静态成员,必须通过对象名。
15 静态成员函数主要用来访问同一类中的静态数据成员,维护对象之间共享的数据。
16 之所以在静态成员函数中访问类的非静态成员需要指明对象,是因为对静态成员函数的调用是没有目的对象的,因此不能像非静态成员函数那样,隐含地通过目的对象访问类的非静态成员。
类的静态成员
复制代码
复制代码
1 有元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。
2 有元关系就是一个类主动声明哪些其他类或函数是它的朋友,进而给它们提供对本类的访问特许。
3 也就是说,通过有元关系,一个普通函数或者类的成员函数可以访问封装于另外一个类中数据。
4 从一定程度上讲,有元是对数据隐蔽和封装的破坏。
5 友元函数是在类中用关键字friend修饰的非成员函数。
6 在它的函数体中可以通过对象名访问类的私有和保护成员。
7 若A类为B类的有元类,则A类的所有成员函数都是B类的有元函数,都可以访问B类的私有和保护成员。
8 有元关系是不能传递的;有元关系是单向的;有元关系是不能被继承的。
类的有元
复制代码
复制代码
1 常对象必须进行初始化,而且不能被更新。
2 声明常对象的语法形式为:
3     const 类型说明符 对象名;
4 在声明常对象时,把const关键字放在类型名后面也是允许的,不过人们更习惯把const写在前面。
5 如果仅以const关键字为区分对成员函数重载,那么通过非const的对象调用该函数,两个重载的函数都可以与之匹配,这时编译器将选择最近的函数——不带const关键字的函数。
6 使用const说明的数据成员为常数据成员,如果在一个类中说明了常数据成员,那么任何函数中都不能对该成员赋值。
7 如果在声明引用时用const修饰,被声明的引用就是常引用。常引用所引用的对象不能被更新。
8 常引用的声明形式如下:
9     const 类型说明符 &引用名;
共享数据的保护
复制代码
复制代码
1 文件作用域中定义的变量,默认情况下都是外部变量,但在其他文件中如果需要使用这一变量,需要用extern关键字加以声明。
2 对外部变量的声明可以是定义性声明,即在声明的同时定义(分配内存,初始化),也可以是引用性声明(引用在别处定义的变量)。
3 在所有类之外声明的函数(也就是非成员函数),都是具有文件作用域的,如果没有特殊说明,这样的函数都可以在不同的编译单元中被调用,只要在调用之前进行引用性声明(即声明函数原型)即可。
4 当然也可以在声明函数原型或定义函数时用extern修饰,其效果与不加修饰的默认状态是一样的。
5 static在局部作用域、类作用域和文件作用域时,具有不尽相同的作用。一个共同点是,凡是被static修饰的变量,都具有静态生存期(不管不使用static关键字时它们的生存期如何)。
6 条件编译可以使同一个源程序在不同的编译条件下产生不同的目标代码。
7 defined是一个预处理操作符,而不是指令,因此不要以#开头。
8 defined操作符使用的形式为:
9     defined(标识符)
多文件结构和编译预处理命令
复制代码

第七章 类的继承

复制代码
 1 类的继承,是新的类从已有类那里得到已有的特性。
 2 从已有类产生新类的过程就是类的派生。
 3 类的派生实际是一种演化、发展过程,即通过扩展、更改和特殊化,从一个已知类出发建立一个新类。
 4 在C++中,派生类的一般定义语法为:
 5 class 派生类名:继承方式 基类名1,继承方式 基类名2,...,继承方式 基类名n
 6 {
 7     派生类成员声明;
 8 };
 9 一个派生类,可以同时有多个基类,这种情况称为多继承。
10 一个派生类只有一个直接基类的情况,称为单继承。
11 直接参与派生的基类称为直接基类,基类的基类甚至更高的基类称为间接基类。
12 继承方式规定了如何访问从基类继承的成员。
13 派生类成员是指除了从基类继承来的所有成员外,新增加的数据和函数成员。
14 在C艹的类继承中,第一步是将基类的成员全盘接收,这样,派生类实际是就包含了它的全部基类中除构造和析构之外的所有成员。在派生过程中构造函数和析构函数都不被继承。
15 对基类成员的改造包括两个方面,一个是基类成员的访问控制问题,主要依靠派生类定义时的继承方式来控制。另一个是对基类数据或函数成员的覆盖或隐藏。
16 如果派生类声明一个和某基类成员同名的新成员,派生的新成员就隐藏了外层同名成员。
17 派生类成员的加入是继承与派生机制的核心,是保证派生类在功能上有所发展的关键。可以根据实际需要,给派生类添加适当的数据和函数成员,来实现必要的新增功能。
基类与派生类
复制代码
复制代码
1 类的继承方式一public(公有继承)、protected(保护继承)和private(私有继承)三种。
2 当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。
3 无论是派生类的成员函数派生类的对象都无法直接访问基类的私有成员。
4 当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。
5 保护继承中,基类的公有和保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。
访问控制
复制代码
1 类型兼容规则是在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。
2 在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
3 根据类型兼容规则,可以在基类对象出现的场合使用派生类对象进行替代,但是替代之后派生类仅仅发挥出基类的作用。
类型兼容规则
复制代码
 1 由于基类的构造函数和析构函数不能被继承,在派生类中,如果对派生类新增的成员进行初始化,就必须为派生类添加新的构造函数。但是派生类的构造函数只负责对派生类新增的成员进行初始化,对所有从基类继承下来成员,其初始化工作还是由基类的构造函数完成。同样,对派生类对象的扫尾、清理工作也需要加入信的析构函数。
 2 构造派生类的对象时,就要对基类的对象成员和新增成员进行初始化。
 3 如果对基类初始化时,需要调用基类的带有形参表的构造函数时,派生类就必须声明构造函数,提供一个将参数传递给基类构造函数的途径,保证基类进行初始化时能够获得必要的数据。
 4 派生类构造函数执行的一般顺序如下:
 51)调用基类构造函数,调用顺序按它们被继承时声明的顺序(从左向右);
 62)对派生类新增的成员初始化,初始化顺序按照它们在类中声明的顺序;
 73)执行派生类的构造函数体中的内容。
 8 “先祖先”调用基类构造函数按照它们被继承时声明的顺序(从左向右);
 9 “再客人”对内嵌成员对象进行初始化,初始化顺序按照它们在类中声明的顺序;
10 “后自己”执行派生类的构造函数体的内容。
11 构造函数的参数表中给出了基类及内嵌成员对象所需的全部参数,在冒号之后,分别列出了各个基类及内嵌对象名和各自的参数。
12 可以用派生类对象去初始化基类的引用。当函数的形参是基类的引用时,实参可以是派生类对象。
13 析构函数的执行次序和构造函数正好严格相反,首先执行析构函数的函数体,然后对派生类新增的类类型的成员对象进行清理,最后对所有从基类继承来的成员进行清理。
派生类的构造和析构函数
复制代码
1 如果某派生类的多个基类拥有同名的成员,同时,派生类又新增这样的同名成员,在这种情况下,派生类成员将隐藏所有基类的同名成员。
2 如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些直接基类中,从上一级基类继承来的成员就拥有相同的名称,因此派生类中也就会产生同名现象,对这种类型的同名成员也要使用作用域分辨符来唯一标识,而且必须用直接基类来进行限定。
3 将共同基类设置为虚基类,这时从不同的路径继承过来的同名数据成员在内存中就只有一个,同一个函数名也只有一个映射。
派生类成员的标识与访问

作业六 继承

判断题

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

答案:正确

体会:在C++中,protected保护继承是一种继承方式,它允许派生类访问其基类中被声明为protected和public的成员,但是不能访问其基类中被声明为private的成员。与公有继承不同的是,对于protected保护继承,派生类可以访问基类中被声明为protected的成员,这就相当于是一种受限制的公有继承。

对于垂直访问,也就是在保护继承关系中,通过派生类对象可以直接访问基类对象的成员函数,这种情况下确实与公有继承等同。然而,在水平访问方面,也就是在保护继承关系中,不同于公有继承,派生类对象不能直接访问基类对象的同名成员函数。此时,如果派生类定义了一个同名的成员函数,那么这个函数将会隐藏基类中的同名成员函数,并且只有在派生类中通过作用域运算符显式地指明要使用基类中的同名成员函数时,才能够访问。因此,在水平访问方面,保护继承等同于私有继承。

单选题

复制代码
下列程序的执行结果为

#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
复制代码

答案:C

体会:在程序中,类B是公有继承自类A的,所以在创建类B对象时,会先调用类A的构造函数,输出1,然后调用类B的构造函数,输出3。程序结束时,按照构造函数调用的逆序,先调用类B的析构函数,输出4,然后调用类A的析构函数,输出2。因此,最终的输出结果为“1324”。

下列关于派生类构造函数和析构函数的说法中,错误的是
A.派生类的构造函数会隐含调用基类的构造函数

B.如果基类声明了带有形参表的构造函数,则派生类就应当声明构造函数

C.在建立派生类对象时,先调用基类的构造函数,再调用派生类的构造函数

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

答案:D

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

函数题 私有继承派生类使用基类的成员函数

复制代码
按要求完成下面的程序:

1、定义一个Animal类,成员包括:

(1)整数类型的私有数据成员m_nWeightBase,表示Animal的体重;

(2)整数类型的保护数据成员m_nAgeBase,表示Animal的年龄;

(3)公有函数成员set_weight,用指定形参初始化数据成员m_nWeightBase;

(4)公有成员函数get_weight,返回数据成员m_nWeightBase的值;

(5)公有函数成员set_age,用指定形参初始化数据成员m_nAgeBase;

2、定义一个Cat类,私有继承自Animal类,其成员包括:

(1)string类型的私有数据成员m_strName;

(2)带参数的构造函数,用指定形参对私有数据成员进行初始化
;
(3)公有的成员函数set_print_age,功能是首先调用基类的成员函数set_age设置数据成员m_nAgeBase的值为5,接着输出成员m_strName的值,然后输出“, age = ”,最后输出基类的数据成员m_nAgeBase的值。具体输出格式参见main函数和样例输出。

(4)公有的成员函数set_print_weight,功能是首先调用基类的成员函数set_weight设置数据成员nWeightBase的值为6,接着输出成员m_strName的值,然后输出“, weight = ”,最后调用基类的成员函数get_weight输出基类的数据成员m_nAgeBase的值。具体输出格式参见main函数和样例输出。

类和函数接口定义:
参见题目描述。
裁判测试程序样例:
#include <iostream>
#include <string>
using namespace std;

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

int main()
{
    Cat cat("Persian");     //定义派生类对象cat
    cat.set_print_age();
    cat.set_print_weight(); //派生类对象调用自己的公有函数
    return 0;
}

输入样例:
本题无输入。

输出样例:
Persian, age = 5
Persian, weight = 6
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
问题描述
复制代码
复制代码
class Animal{
    public:
    void set_weight(int);
    int get_weight();
    void set_age(int);
    int get_age();
    protected:
    int m_nAgeBase;
    int m_nWeightBase;
};
void Animal::set_weight(int a)
{
    m_nWeightBase=a;
}
int Animal::get_weight(void)
{
    return m_nWeightBase;
}
void Animal::set_age(int a)
{
    m_nAgeBase=a;
}
int Animal::get_age(void)
{
    return m_nAgeBase;
}
class Cat:private Animal
{
    private:
    string m_strName;
    public:
    Cat(string name);
    void set_print_age();
    void set_print_weight();
};
Cat::Cat(string m_strName_){
    m_strName=m_strName_;
}
void Cat::set_print_age()
{
    set_age(5);
     cout << m_strName << ", age = " << m_nAgeBase << endl;
}
void Cat::set_print_weight()
{
    set_weight(6);
    cout << m_strName << ", weight = " << m_nWeightBase << endl;
}
源代码
复制代码

实验二 类的继承与派生

复制代码
以点类Point为基类公有派生圆类Circle,main(void)函数完成对其的测试。

Point类结构说明:
Point类的数据成员包括:
①私有数据成员:X坐标x(double型),Y坐标y(double型)。
Point类成员函数包括:
①有参构造函数Point(double, double)和拷贝构造函数Point(const  Point  &),其中有参构造函数参数默认值为0,输出信息“Point Constructor run”,拷贝构造函数输出信息“Point CopyConstructor run”
②析构函数,析构函数输出信息“Point Destructor run”
③公有函数成员:void  setX(double)和double getX() const分别返回和设置X坐标
④公有函数成员:void  setY(double)和double getY() const分别返回和设置Y坐标
⑤公有成员函数void show() const用于显示点的坐标信息,显示格式为:(x,y)
⑥公有成员函数double area() const用于计算点的面积,点的面积为0
Circle类结构说明:
公有派生圆类Circle以点类Point为基类,相较于点类Point,Circle类的结构说明如下:
Circle类的数据成员包括:
①私有数据成员:X坐标x继承自Point类,Y坐标y继承自Point类。
②静态数据常量PI(double型),其值为3.14159
③私有数据成员:半径radius(double型)。
Circle类成员函数包括:
①定义有参构造函数①定义有参构造函数Circle(double, double, double)和拷贝构造函数Circle(Circle &),其中有参构造函数参数包括圆心坐标和半径,圆心调用Point类构造函数进行构造,各参数默认值为0,输出信息“Circle Constructor run”,拷贝构造函数输出信息“Circle CopyConstructor run”
②定义析构函数,析构函数输出信息“Circle Destructor run”
③定义公有函数成员:void setRadius(double)和double getRadius() const分别返回和设置radius
④定义公有成员函数double circumference() const用于计算圆的周长
⑤重载double area() const用于计算圆的面积
⑥重载void show() const用于显示圆的信息,显示格式为:
(圆心X坐标,圆心Y坐标),Radius=半径
裁判测试程序样例:
#include <iostream>
using namespace std;
class Point{
    protected:
      double x;
      double y;
    public:
      Point(double xv=0,double yv=0);//有参构造
      Point(const Point &p);         //拷贝构造
      ~Point();                      //析构函数
      void show() const;             //显示Point信息
      double area()const;            //面积=0
      void setX(double xv);          //设置X坐标
      void setY(double yv);          //设置Y坐标
      double getX() const;           //获取X坐标
      double getY() const;           //获取Y坐标
};
//有参构造
Point::Point(double xv,double yv){
    x=xv;
    y=yv;
    cout<<"Point Constructor run"<<endl;
}
//拷贝构造
Point::Point(const Point &p){
    x=p.x;
    y=p.y;
    cout<<"Point CopyConstructor run"<<endl;
}
//析构函数
Point::~Point(){
    cout<<"Point Destructor run"<<endl;
}
//显示Point
void Point::show() const{
    cout<<"("<<x<<","<<y<<")";
}
//设置X坐标
void Point::setX(double xv){
    x=xv;
}
//设置Y坐标
void Point::setY(double yv){
    y=yv;
}
//面积函数
double Point::area() const{
    return 0;
}
//获取X坐标
double Point::getX() const{
    return x;
}
//获取Y坐标
double Point::getY() const{
    return y;
}

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

int main(void){
    double  r;
    cin>>r;
    Circle c1(1,2,4),c2,c3(c1);
    c1.show();
    cout<<endl<<"Area="<<c1.area()<<endl<<"Circumference="<<c1.circumference()<<endl;
    c2.show();
    cout<<endl<<"Area="<<c2.area()<<endl<<"Circumference="<<c2.circumference()<<endl;
    c3.setRadius(r);
    c3.show();
    cout<<endl<<"Area="<<c3.area()<<endl<<"Circumference="<<c3.circumference()<<endl;
    return 0;
}
输入样例:
6.0
输出样例:
Point Constructor run
Circle Constructor run
Point Constructor run
Circle Constructor run
Point CopyConstructor run
Circle CopyConstructor run
(1,2),Radius=4
Area=50.2654
Circumference=25.1327
(0,0),Radius=0
Area=0
Circumference=0
(1,2),Radius=6
Area=113.097
Circumference=37.6991
Circle Destructor run
Point Destructor run
Circle Destructor run
Point Destructor run
Circle Destructor run
Point Destructor run
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
以点类Point为基类设计圆类Circle
复制代码
复制代码
class Circle:public Point
{
private:
    double x,y;
    double radius;
    public:
    static double PI;
    Circle(double a=0,double b=0,double c=0);
    Circle(Circle &obj);
    ~Circle();
    void setRadius(double);
    double getRadius() const;
    double circumference() const;
    double area() const;
    void show() const;

};
double Circle::PI=3.14159;
Circle::Circle(double a,double b,double c):Point(a,b),radius(c){
    x=a;
    y=b;
    cout<<"Circle Constructor run"<<endl;
    }
Circle::Circle(Circle &p):Point(p)
{
    x=p.x;
    y=p.y;
    radius=p.radius;
    //cout<<x<<y<<radius<<endl;
    cout<<"Circle CopyConstructor run"<<endl;
}
Circle::~Circle(void){
    cout<<"Circle Destructor run"<<endl;
}
void Circle::setRadius(double a)
{
    radius=a;
    //cout<<a<<endl;
}
double Circle::getRadius() const
{
    return radius;
}
double Circle::circumference() const
{
    double c;
    c=radius*PI*2;
    return c;
}
double Circle::area() const
{
    double s;
    s=radius*radius*PI;
    return s;
}
void Circle::show() const
{
    cout<<"("<<x<<","<<y<<")"<<",Radius="<<radius;
}
源代码1
复制代码
复制代码
以人类Person为基类公有派生学生类Student和教师类Teacher,main(void)函数完成对其的测试。

Person类结构说明:
Person类的数据成员包括:
①私有数据成员:身份证号no(char [20]型),姓名name(char [20]型)。
Person类成员函数包括:
①有参构造函数Person(char *, char *)和拷贝构造函数Person(const  Person  &),其中有参构造函数参数默认值为空串或0(当参数为NULL时视为空串处理),输出信息“Person Constructor run”,拷贝构造函数输出信息“Person CopyConstructor run”
②析构函数,析构函数输出信息“Person Destructor run”
③公有函数成员:void  setNo(char *)和char * getNo()分别返回和设置身份证号no(当参数为NULL时视为空串处理)
④公有函数成员:void  setName(char *)和char*  getName()分别返回和设置姓名name(当参数为NULL时视为空串处理)
⑤公有成员函数void show() const用于显示人的基本信息,显示格式为:
No=身份证号,Name=姓名
Student类结构说明:
公有派生学生类Student以人类Person为基类,Student类的结构说明如下:
Student类的数据成员包括:
①私有数据成员:身份证号no继承自Person类,姓名name继承自Person类。
②私有数据成员:学号sNo(char [10]型)
③私有数据成员:班级className(char [20]型)
④私有数据成员:成绩score(double型)
Student类成员函数包括:
①有参构造函数Student(char *, char *, char *,char *, double)和拷贝构造函数Student(const  Student  &),其中有参构造函数参数默认值为空串或0.0(当参数为NULL时视为空串处理),输出信息“Student Constructor run”,拷贝构造函数输出信息“Student CopyConstructor run”
②析构函数,析构函数输出信息“Student Destructor run”
③公有函数成员:void  setSNo(char *)和char * getSNo()分别返回和设置学号sNo(当参数为NULL时视为空串处理)
④公有函数成员:void  setClassName(char *)和char*  getClassName()分别返回和设置班级className(当参数为NULL时视为空串处理)
⑤公有函数成员:void  setScore(double )和double  getScore()分别返回和设置成绩score(当参数缺省时视为0.0)
⑥公有成员函数void show() const用于显示学生的基本信息,显示格式为:
No=身份证号,Name=姓名
SNo=学号,ClassName=班级,Score=成绩
其中,成绩四舍五入取整。
Teacher类结构说明:
公有派生教师类Teacher以人类Person为基类,Teacher类的结构说明如下:
Teacher类的数据成员包括:
①私有数据成员:身份证号no继承自Person类,姓名name继承自Person类。
②私有数据成员:工号tNo(char [10]型)
③私有数据成员:部门班级departmentName(char [20]型)
④私有数据成员:工资wages(double型)
Teacher类成员函数包括:
①有参构造函数Teacher(char *, char *, char *,char *, double)和拷贝构造函数Teacher(const  Teacher  &),其中有参构造函数参数默认值为空串或0.0(当参数为NULL时视为空串处理),输出信息“Teacher Constructor run”,拷贝构造函数输出信息“Teacher CopyConstructor run”
②析构函数,析构函数输出信息“Teacher Destructor run”
③公有函数成员:void  setTNo(char *)和char * getTNo()分别返回和设置工号tNo(当参数为NULL时视为空串处理)
④公有函数成员:void  setDepartmentName(char *)和char*  getDepartmentName()分别返回和设置部门departmentName(当参数为NULL时视为空串处理)
⑤公有函数成员:void  setWages(double )和double  getWages()分别返回和设置工资wages(当参数缺省时视为0.0)
⑥公有成员函数void show() const用于显示教师的基本信息,显示格式为:
No=身份证号,Name=姓名
TNo=工号,DepartmentName=部门,Wages=工资
其中,工资四舍五入取整。
裁判测试程序样例:
#include <iostream>
using namespace std;
//Person类
class Person{
protected:
    char no[20];                   //身份证号
    char name[20];                //姓名
public:
    Person(char *str1=0,char *str2=0);//有参构造
    Person(const Person &p);        //拷贝构造
    ~Person();                    //析构函数
    void show() const;             //显示Person信息
    void setNo(char *str);          //设置身份证号
    void setName(char *str);        //设置姓名
    char *getNo();                //获取身份证号
    char* getName();              //获取姓名
};
//有参构造
Person::Person(char *str1,char *str2){
    int i;
    i=0;
    if(str1!=0)
        while(str1[i]!='\0'){
            no[i]=str1[i];
            i++;
        }
    no[i]='\0';
    i=0;
    if(str2!=0)
        while(str2[i]!='\0'){
            name[i]=str2[i];
            i++;
        }
    name[i]='\0';
    cout<<"Person Constructor run"<<endl;
}
//拷贝构造
Person::Person(const Person &p){
    int i=0;
    while(p.no[i]!='\0'){
        no[i]=p.no[i];
        i++;
    }
    no[i]='\0';
    i=0;
    while(p.name[i]!='\0'){
        name[i]=p.name[i];
        i++;
    }
    name[i]='\0';
    cout<<"Person CopyConstructor run"<<endl;
}
//析构函数
Person::~Person(){
    cout<<"Person Destructor run"<<endl;
}
//显示Person
void Person::show() const{
    cout<<"No="<<no<<",Name="<<name<<endl;
}
//设置身份证号
void Person::setNo(char *str){
    int i=0;
    if (str)
        while(str[i]!='\0'){
            no[i]=str[i];
            i++;
        }
    no[i]='\0';
}
//设置姓名
void Person::setName(char *str){
    int i=0;
    if (str)
        while(str[i]!='\0'){
            name[i]=str[i];
            i++;
        }
    name[i]='\0';
}
//获取身份证号
char* Person::getNo(){
    return no;
}
//获取姓名
char* Person::getName(){
    return name;
}

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

//主函数
int main(){
    char s1[19]="130502190001010332";
    char s2[20]="doublebest";
    char s3[19]="20181234";
    char s4[20]="铁1801";
    double value=60.67;
    Student stu1(s1,s2,s3,s4,value);
    stu1.show();
    Student stu2=stu1;
    cin.getline(s3,10,'\n');
    cin.getline(s4,20,'\n');
    cin>>value;
    stu2.setSNo(s3);
    stu2.setClassName(s4);
    stu2.show();
    Teacher t1(s1,s2,s3,s4,value);
    t1.show();
    Teacher t2=t1;
    t2.setTNo(s3);
    t2.setDepartmentName(s3);
    t2.show();
    return 0;
}
输入样例:
20181234
铁1801
45.45
输出样例:
Person Constructor run
Student Constructor run
No=130502190001010332,Name=doublebest
SNo=20181234,ClassName=铁1801,Score=61
Person CopyConstructor run
Student CopyConstructor run
No=130502190001010332,Name=doublebest
SNo=20181234,ClassName=铁1801,Score=61
Person Constructor run
Teacher Constructor run
No=130502190001010332,Name=doublebest
TNo=20181234,DepartmentName=铁1801,Wages=45
Person CopyConstructor run
Teacher CopyConstructor run
No=130502190001010332,Name=doublebest
TNo=20181234,DepartmentName=20181234,Wages=45
Teacher Destructor run
Person Destructor run
Teacher Destructor run
Person Destructor run
Student Destructor run
Person Destructor run
Student Destructor run
Person Destructor run
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
以人类Person为基类设计学生类Student和教师类
复制代码
复制代码
class Student:public Person
{
    char sNo[10];
    char className[20];
    double score;
    public:
    Student(char *str1=0,char *str2=0,char *str3=0,char *str4=0,double a=0);
    Student(const Student &p);
    ~Student();
    void  setSNo(char *str);
    void  setClassName(char *str);
    void  setScore(double a);
    char * getSNo();
    char*  getClassName();
    double  getScore();
    void show() const;
};
Student::Student(char *str1,char *str2,char *str3,char *str4,double a):Person(str1,str2),sNo(),className(),score(a)
{
    setSNo(str3);
    setClassName(str4);
    int b=(int)a;
    if((a-b)>0.5){b=b+1;}
    score=b;
    cout<<"Student Constructor run"<<endl;
}
Student::Student(const Student &p):Person(p)
{
    int i=0;
    while(p.no[i]!='\0'){
        no[i]=p.no[i];
        i++;
    }
    no[i]='\0';
    i=0;
    while(p.name[i]!='\0'){
        name[i]=p.name[i];
        i++;
    }
    name[i]='\0';
    i=0;
    while(p.sNo[i]!='\0'){
        sNo[i]=p.sNo[i];
        i++;
    }
    sNo[i]='\0';
    i=0;
    while(p.className[i]!='\0'){
        className[i]=p.className[i];
        i++;
    }
    className[i]='\0';
    score=p.score;
    cout<<"Student CopyConstructor run"<<endl;
}
Student::~Student(void){
    cout<<"Student Destructor run"<<endl;
}
void Student::show() const{
    cout<<"No="<<no<<",Name="<<name<<endl;
    cout<<"SNo="<<sNo<<",ClassName="<<className<<",Score="<<score<<endl;
}
void  Student::setSNo(char *str)
{
    int i=0;
    if (str)
        while(str[i]!='\0'){
            sNo[i]=str[i];
            i++;
        }
    sNo[i]='\0';
}
void  Student::setClassName(char *str)
{
    int i=0;
    if (str)
        while(str[i]!='\0'){
            className[i]=str[i];
            i++;
        }
    className[i]='\0';
}
void  Student::setScore(double a)
{
    int b=(int)a;
    printf("%d\n",b);
    score=b;
}
char* Student::getSNo()
{
    return sNo;
}
char*  Student::getClassName()
{
    return className;
}
double  Student::getScore()
{

    return score;
}
class Teacher:public Person
{
    char tNo[10];
    char departmentName[20];
    double wages;
    public:
    Teacher(char *str1=0,char *str2=0,char *str3=0,char *str4=0,double a=0);
    Teacher(const Teacher &p);
    ~Teacher();
    void  setTNo(char *str);
    void  setDepartmentName(char *str);
    void  setWages(double a);
    char * getTNo();
    char*  getDepartmentName();
    double  getWages();
    void show() const;
};
Teacher::Teacher(char *str1,char *str2,char *str3,char *str4,double a):Person(str1,str2),tNo(),departmentName(),wages(a)
{
    setTNo(str3);
    setDepartmentName(str4);
    int b=(int)a;
    if((a-b)>0.5){b=b+1;}
    wages=b;
    cout<<"Teacher Constructor run"<<endl;
}
Teacher::Teacher(const Teacher &p):Person(p)
{
    int i=0;
    while(p.no[i]!='\0'){
        no[i]=p.no[i];
        i++;
    }
    no[i]='\0';
    i=0;
    while(p.name[i]!='\0'){
        name[i]=p.name[i];
        i++;
    }
    name[i]='\0';
    i=0;
    while(p.tNo[i]!='\0'){
        tNo[i]=p.tNo[i];
        i++;
    }
    tNo[i]='\0';
    i=0;
    while(p.departmentName[i]!='\0'){
        departmentName[i]=p.departmentName[i];
        i++;
    }
    departmentName[i]='\0';
    wages=p.wages;
    cout<<"Teacher CopyConstructor run"<<endl;
}
Teacher::~Teacher(void){
    cout<<"Teacher Destructor run"<<endl;
}
void Teacher::show() const{
    cout<<"No="<<no<<",Name="<<name<<endl;
    cout<<"TNo="<<tNo<<",DepartmentName="<<departmentName<<",Wages="<<wages<<endl;
}
void  Teacher::setTNo(char *str)
{
    int i=0;
    if (str)
        while(str[i]!='\0'){
            tNo[i]=str[i];
            i++;
        }
    tNo[i]='\0';
}
void  Teacher::setDepartmentName(char *str)
{
    int i=0;
    if (str)
        while(str[i]!='\0'){
            departmentName[i]=str[i];
            i++;
        }
    departmentName[i]='\0';
}
void  Teacher::setWages(double a)
{
    int b=(int)a;
    if((a-b)>0.5){b=b+1;}
    wages=b;
}
char* Teacher::getTNo()
{
    return tNo;
}
char*  Teacher::getDepartmentName()
{
    return departmentName;
}
double  Teacher::getWages()
{
    return wages;
}
源代码2
复制代码
复制代码
以点类Point为基类公有派生圆类Circle,并以圆类Circle为基类公有派生球类Sphere,main(void)函数完成对其的测试。

Point类结构说明:
Point类的数据成员包括:
①私有数据成员:X坐标x(double型),Y坐标y(double型)。
Point类成员函数包括:
①有参构造函数Point(double, double)和拷贝构造函数Point(const  Point  &),其中有参构造函数参数默认值为0,输出信息“Point Constructor run”,拷贝构造函数输出信息“Point CopyConstructor run”
②析构函数,析构函数输出信息“Point Destructor run”
③公有函数成员:void  setX(double)和double getX() const分别返回和设置X坐标
④公有函数成员:void  setY(double)和double getY() const分别返回和设置Y坐标
⑤公有成员函数void show() const用于显示点的坐标信息,显示格式为:(x,y)
⑥公有成员函数double area() const用于计算点的面积,点的面积为0
###Circle类结构说明:

公有派生圆类Circle以点类Point为基类,相较于点类Point,Circle类的结构说明如下:
Circle类的数据成员包括:
①私有数据成员:X坐标x继承自Point类,Y坐标y继承自Point类。
②私有数据成员:半径radius(double型)
③静态数据常量PI(double型),其值为3.14159
Circle类成员函数包括:
①定义有参构造函数Circle(double, double, double)和拷贝构造函数Circle(Circle &),其中有参构造函数参数包括圆心坐标和半径,圆心调用Point类构造函数进行构造,各参数默认值为0,输出信息“Circle Constructor run”,拷贝构造函数输出信息“Circle CopyConstructor run”
②定义析构函数,析构函数输出信息“Circle Destructor run”
③定义公有函数成员:void setRadius(double)和double getRadius() const分别返回和设置radius
④定义公有成员函数double circumference() const用于计算圆的周长
⑤重载double area() const用于计算圆的面积
⑥重载void show() const用于显示圆的信息,显示格式为:
(圆心X坐标,圆心Y坐标),Radius=半径
Sphere类结构说明:
公有派生球类Sphere以圆类Circle为基类,Sphere类的结构说明如下:
Sphere类的数据成员包括:
①私有数据成员:X坐标x继承自Circle类,Y坐标y继承自Circle类,半径radius继承自Circle类。
②静态数据常量PI继承自Circle类。
Sphere类成员函数包括:
①定义有参构造函数Sphere(double, double, double)和拷贝构造函数Sphere(Sphere &),其中有参构造函数参数包括圆心坐标和半径,圆心调用Point类构造函数进行构造,各参数默认值为0,输出信息“Sphere Constructor run”,拷贝构造函数输出信息“Sphere CopyConstructor run”
②定义析构函数,析构函数输出信息“Sphere Destructor run”
③定义公有成员函数double volume() const用于计算球的体积
④重载double area() const用于计算球的表面积
裁判测试程序样例:
#include <iostream>
using namespace std;
class Point{
    protected:
      double x;
      double y;
    public:
      Point(double xv=0,double yv=0);//有参构造
      Point(const Point &p);         //拷贝构造
      ~Point();                      //析构函数
      void show() const;             //显示Point信息
      double area()const;            //面积=0
      void setX(double xv);          //设置X坐标
      void setY(double yv);          //设置Y坐标
      double getX() const;           //获取X坐标
      double getY() const;           //获取Y坐标
};
//有参构造
Point::Point(double xv,double yv){
    x=xv;
    y=yv;
    cout<<"Point Constructor run"<<endl;
}
//拷贝构造
Point::Point(const Point &p){
    x=p.x;
    y=p.y;
    cout<<"Point CopyConstructor run"<<endl;
}
//析构函数
Point::~Point(){
    cout<<"Point Destructor run"<<endl;
}
//显示Point
void Point::show() const{
    cout<<"("<<x<<","<<y<<")";
}
//设置X坐标
void Point::setX(double xv){
    x=xv;
}
//设置Y坐标
void Point::setY(double yv){
    y=yv;
}
//面积函数
double Point::area() const{
    return 0;
}
//获取X坐标
double Point::getX() const{
    return x;
}
//获取Y坐标
double Point::getY() const{
    return y;
}

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

//主函数
int main(void){
    double  r;
    cin>>r;
    Sphere s1(1,2,4),s2(s1);
    s1.show();
    cout<<endl<<"Area="<<s1.area()<<endl<<"Volume="<<s1.volume()<<endl;
    s2.setRadius(r);
    s2.Circle::show();
    cout<<endl<<"Area="<<s2.Circle::area()<<endl<<"Circumference="<<s2.Circle::circumference()<<endl;
    return 0;
}
输入样例:
6.0
输出样例:
Point Constructor run
Circle Constructor run
Sphere Constructor run
Point CopyConstructor run
Circle CopyConstructor run
Sphere CopyConstructor run
(1,2),Radius=4
Area=201.062
Volume=268.082
(1,2),Radius=6
Area=113.097
Circumference=37.6991
Sphere Destructor run
Circle Destructor run
Point Destructor run
Sphere Destructor run
Circle Destructor run
Point Destructor run
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
以圆类Circle为基础设计球类Sphere
复制代码
复制代码
class Circle:public Point
{
private:
    double x,y;
    double radius;
    public:
    static double PI;
    Circle(double a=0,double b=0,double c=0);
    Circle(Circle &obj);
    ~Circle();
    void setRadius(double);
    double getRadius() const;
    double circumference() const;
    double area() const;
    void show() const;

};
double Circle::PI=3.14159;
Circle::Circle(double a,double b,double c):Point(a,b),radius(c){
    x=a;
    y=b;
    cout<<"Circle Constructor run"<<endl;
    }
Circle::Circle(Circle &p):Point(p)
{
    x=p.x;
    y=p.y;
    radius=p.radius;
    //cout<<x<<y<<radius<<endl;
    cout<<"Circle CopyConstructor run"<<endl;
}
Circle::~Circle(void){
    cout<<"Circle Destructor run"<<endl;
}
void Circle::setRadius(double a)
{
    radius=a;
    //cout<<a<<endl;
}
double Circle::getRadius() const
{
    return radius;
}
double Circle::circumference() const
{
    double c;
    c=radius*PI*2;
    return c;
}
double Circle::area() const
{
    double s;
    s=radius*radius*PI;
    return s;
}
void Circle::show() const
{
    cout<<"("<<x<<","<<y<<")"<<",Radius="<<radius;
}
class Sphere:public Circle
{
    double x,y,radius;
    //static double PI=3.14159;
    public:
    Sphere(double a=0, double b=0, double c=0);
    Sphere(Sphere &);
    ~Sphere();
    double volume() const;
    double area() const;
};
Sphere::Sphere(double a, double b, double c):Circle(a,b,c)
{
    x=a;y=b;radius=c;
    cout<<"Sphere Constructor run"<<endl;
}
Sphere::Sphere(Sphere &obj):Circle(obj)
{
    x=obj.x;
    y=obj.y;
    radius=obj.radius;
    cout<<"Sphere CopyConstructor run"<<endl;
}
Sphere::~Sphere(void)
{
    cout<<"Sphere Destructor run"<<endl;
}
double Sphere::area() const
{
    double s;
    s=radius*radius*PI*4;
    return s;
}
double Sphere::volume() const
{
    double v;
    v=4*radius*radius*radius*PI/3.0;
    
    return v;
}
源代码3
复制代码

总结:

继承与派生是面向对象编程中的两个重要概念,它们是实现软件重用和增强拓展性的关键。

继承是指在定义新的类时,可以使用已经存在的类的属性和方法来进行定义。通过继承,我们可以构建出一种新的类,这个新类不仅包含了原有类的所有特性,还可以自行添加或者修改一些独有的特性。继承是面向对象编程中的一个核心思想,通过继承,我们可以避免代码的冗余,提高代码的可读性和可维护性。

派生是指从基类中派生出新的类,并且新的类可以通过继承获得基类的成员变量和成员函数。通过派生,我们可以在原有类的基础上,添加新的功能和行为,使得程序具有更好的拓展性和复用性。

在使用继承和派生时,需要注意以下几点:

  1. 继承应该合理使用,过度的继承会导致程序结构混乱、耦合度加强,不利于程序的维护和拓展。

  2. 在进行派生时,必须明确基类和派生类的关系。通常,采取公有继承是最为常见的方式,但是在一些特定的场景下,也可以选择私有或者保护继承。

  3. 在派生类的设计中,需要注意虚函数和多态的使用,这有利于提高代码的可拓展性和可重用性。

  4. 在派生过程中,需要明确基类和派生类成员变量的作用域和继承关系,合理使用作用域限定符和显式调用基类方法等技术手段,可以避免出现不必要的错误。

posted @   寒心小呆  阅读(278)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
点击右上角即可分享
微信分享提示