第9章 继承和派生习题(详解)

2024-01-01 23:15:19

在这里插入图片描述

一、选择题

1.下列表示引用的方法中, () 是正确的。已知:int m=10:

A.int &x=m; B.int &y=10; C.int &z; D.float &t=&m;

在C++中,引用是一个变量的别名。正确的引用声明方式应遵循C++的引用规则。

选项分析:

A.`int &x=m;` 这是一个正确的引用声明。`x` 成为 `m` 的引用,`x` 和 `m` 指向同一内存位置。

B.`int &y=10;` 这是一个错误的引用声明。引用必须绑定到一个变量,不能直接绑定到字面值(如10)。

C.`int &z;` 这是一个错误的声明。引用在声明时必须被初始化。

D.`float &t=&m;` 这是一个错误的声明。首先,`&m` 获得的是变量 `m` 的地址,其类型是 `int*` 而不是 `float`。其次,`t` 应该直接绑定到一个 `float` 类型的变量,而不是一个地址。

因此,正确的选项是:

A.`int &x=m;`

2. 在以下类定义中, () 的虚基类

A)类B、类C是类A B)类A是类B、类C C)类B、类C是类D D)类A是类D

class A{
public: 
int x;
A(int a=0){x=a;}
 	};
class B:public virtual A
{ ... };
class C:virtual public A
{  ... };
class D:public B,public C
{   	A x; 
public: 
int m; 
D(int a,int b,int c):B(a),C(b),x(c)  //F
...
};
在C++中,如果一个基类被多个直接或间接派生类通过不同的路径继承,
并且这个基类在继承路径中至少有一次是作为虚基类来继承的,那么这个基类就是虚基类。
如果一个基类包含另一个基类的指针或引用,那么这个基类就被称为虚基类。 选D

3.C++中的类有两种用法:一种是类的实例化,即生成类的对象,并参与系统的运行;另一种是通过 (),派生出新的类。

A.复用 B.继承C.单继承 D.多继承

继承允许新的类接收(或继承)一个或多个类的成员(包括数据成员和成员函数)。
这是代码复用的一个主要手段。选B

4.继承具有 () ,即当基类本身也是某一个类的派生类时,底层的派生类也会自动继承间接基类的成员。

A.规律性 B.传递性 C.重复性 D.多样性

B.传递性
这个特性确保了派生类能够从它的直接基类以及间接基类那里继承属性和方法,允许多层次的抽象和代码重用。

5.在多继承中,公有派生和私有派生对于基类成员在派生类中的可访问性与单继承的规则 () 。

A.完全相同 B.完全不同 C.部分相同,部分不同 D.以上都不对

在多继承中,公有派生和私有派生对于基类成员在派生类中的可访问性的规则与单继承中的规则是完全相同的。
无论是单继承还是多继承,
基类成员的访问控制(public, protected, private)都遵循相同的规则。因此,正确的答案是:
A.完全相同
公有继承(public inheritance)意味着基类的公有成员和保护成员在派生类中保持原有的访问权限,
而私有继承(private inheritance)意味着基类的所有成员在派生类中都变成了私有成员,
无论它们在基类中原来是什么访问权限。

6.派生类的构造函数的成员初始化列表中,不能包含 。

A.基类的构造函数 B.派生类中子对象的初始化
C.基类的子对象初始化 D.派生类中一般数据成员的初始化

在派生类的构造函数的成员初始化列表中可以包含以下元素:
A. 基类的构造函数 - 派生类的构造函数可以(并且在多数情况下应该)
在其成员初始化列表中调用基类的构造函数,以确保基类部分被正确初始化。
B. 派生类中子对象的初始化 - 如果派生类有类类型的成员(子对象),
则可以(也应该)在成员初始化列表中初始化它们。
C. 基类的子对象初始化 - 这一选项是有歧义的。如果指的是基类的成员(即基类中的子对象)
,那么这些在基类构造函数中被初始化,不应直接出现在派生类的成员初始化列表中。
但是,如果这里指的是由基类派生出的另一个基类的对象,
则派生类可以在其成员初始化列表中直接初始化这样的基类的子对象。
D. 派生类中一般数据成员的初始化 -
 派生类的构造函数的成员初始化列表当然可以包含派生类中一般数据成员的初始化。
因此,如果我们解释C为“基类中的子对象”,那么它是不能被派生类的构造函数的成员初始化列表直接初始化的,因为这是基类构造函数的责任。这样看来,C是正确的答案。如果解释C为“由基类派生的其他基类”,那么派生类是可以(并且应该)在其成员初始化列表中初始化这样的对象的。这里需要明确题目中“基类的子对象初始化”的具体含义。
选C

7. 定义派生类时,若不使用关键字显式地规定采用何种继承方式,则默认方式是 。

A. 私有继承 B. 非私有继承 C. 保护继承 D. 公有继承

A 私有继承

8. 在一个派生类的成员函数中,试图调用其基类的成员函数“void f();”,但无法通过编译。这说明 () 。

A. f()是基类的私有成员 B. f()是基类的保护成员
C. 派生类的继承方式是私有 D. 派生类的继承方式为保护

A f()是基类的私有成员

9. 有如下类定义:

class AA{
int a;
public:
AA(int n=0):a(n){}
};
class BB:public AA{
public;
BB(int n).......};

其中横线处缺失部分是 。
A.:a(n){} B.;AA(n){} C.{a(n)}; D.{a=n};

选项B `:AA(n){}` 正确的填写了类 BB 的构造函数,在初始化列表中调用了基类 AA 的构造函数,将 n 作为参数传递给它。这是正确的继承和初始化方式,能够确保派生类 BB 创建时,其基类 AA 的部分也得到了正确的初始化。

以下是各个选项的解释:

A. `:a(n){}` - 这是不合法的,因为 `a` 是基类 AA 的私有成员,不可以在派生类的初始化列表中直接初始化。

B. `:AA(n){}` - 这是正确的。它使用基类的构造函数来初始化基类部分。

C. `{a(n)};` - 这也是不合法的,因为像选项 A,`a` 是私有成员,不能在派生类中直接初始化或访问。

D. `{a=n};` - 这同样是不合法的,因为 `a` 是基类的私有成员,不能在派生类中直接赋值。

因此,正确答案是 B,它正确地使用了基类构造函数初始化基类成员变量。

10. 已知基类 Employee只有一个构造函数,其定义如下:

Employee::Employee(int n):id(n){}
Manager 是 Employee 的派生类,则下列对 Manager 的构造函数定义中,正确的是 。
A.Manager::Manager(int n):id(n){}
B.Manager::Manager(int n){id=n}
C.Manager::Manager(int n):Employee(n){}
D.Manager::Manager(int n){ Employee(n);}

C

11.有如下程序:

#include<iostream>
using namespace std;
class Base{
public;
void fun(){cout<'B';  }
};
class  Derived:public  Base{
public:
void fun()
{   .......        ;                                              
cout<<'D';
}
};
int main()
{  	Derived d;
d.fun();
return 0;
}

若程序的输出结果是 BD, 则画线处缺失的部分是 。
A.fun(); B.Base,fun(); C.Base::fun(); D.Base->fun();

C

12. 关于虚基类的描述中,错误的是 () 。

A, 使用虚基类可以消除由多继承产生的二义性
B. 构造派生类对象时,虚基类的构造函数只被调用一次
C. 声明“class B:virtual public A”,说明类 B为虚基类
D. 建立派生类对象时,首先调用虚基类的构造函数

C. 声明“class B:virtual public A”,说明类 B为虚基类

这个描述是错误的。该声明实际上是指 A 是 B 的虚基类,而不是 B 是某个虚基类。当使用虚继承时,
类 A 将成为类 B 的虚基类,这样做是为了解决多重继承中可能出现的菱形继承问题,
其中一个类可能通过不同的路径多次继承同一个基类。

二、填空题

1. 如果类 A 继承了类 B, 那么类 A 被称为 (基) 类,而类 B 被称为 (派生) 类。
2 . C++的两种继承为: (公有继承) 和 (私有继承) 。
3. 在默认情况下的继承方式为(私有继承) 。
4. 从基类中公有派生一个类时,基类的公有成员就成为派生类的(公有) 成员,而这个基类的保护成员就成为派生类的 (保护) 成员。
5. C++提供了(多继承) 机制,允许一个派生类可以继承多个基类,甚至这些基类是互 不相关的。
6. 类X中的名字N 支配类Y中同名的名字N, 是指类X 以类Y 为它的一个基类,这称为 (支配规则) 。
7. 引进虚基类的目的是 (消除多重继承中由于继承同一个基类而产生的二义性和重复基类成员问题) 。
8. 在一个继承结构中,解决二义性的方法有 (虚继承) 和 (使用作用域运算符) 。

三、阅读程序题

1. 下列程序的输出结果是(3)

#include<iostream>
using namespace std;
class Base
{	int k;
public:
	void set(int n){k=n;}
	int get(){return k;}
};
class Derived:protected Base
{  	int j;
public:
	void set(int m,int n){Base::set(m);j=n;}
	int get(){return Base::get()+j; }
};
int main()
{
	Derived d;
	d.set(1,2);
	cout<<d.get()<<endl;
	return 0;
}

2,下列程序的输出结果是

#include<iostream>
using namespace std;
class A
{
public:
	A(){ cout<<"A";}
	~A(){cout<<"~A"; }
};
class B:public A
{	A *p;
public:
	B()
	{   cout<<"B";
		p=new A();
	}
	~B()
	{   cout<<"~B";
		delete p;
	}
};
int main()
{  	B obj;
	return 0;
}
程序的输出结果是 `ABAB~A~B~A`。

下面是程序执行过程中的详细解释:

1. 在main函数中,创建了一个`B`类的对象`obj`。
2. 调用`B`的构造函数`B()`,由于`B`继承自`A`,首先调用基类`A`的构造函数。
3. 输出`A`。
4. 然后执行`B`的构造函数体,输出`B`。
5. 在`B`的构造函数中,使用`new`创建了一个`A`类的对象,并将其地址赋值给指针`p`,因此会再次调用`A`的构造函数。
6. 输出`A`。
7. 程序接着会离开`B`构造函数的作用域,但此时对象`obj`的生命周期并未结束,故不会调用任何析构函数。
8. 当main函数执行完毕,`obj`的生命周期结束,此时会首先调用`B`类的析构函数`~B()`。
9. 输出`~B`。
10. 在`~B()`中,`delete p;`会删除之前构造函数中创建的`A`对象,因此调用`A`的析构函数。
11. 输出`~A`。
12. 跳出`~B()`的作用域,`B`的析构函数执行完毕,接着调用基类`A`的析构函数。
13. 输出`~A`。

因此,整个程序的输出为`ABAB~A~B~A~A`。

3. 下列程序的输出结果是 。

#include<iostream>
using namespace std;
class A{
public:
	int x;
	A(){ x=100; }
	A(int  i){x=i; }
	void Show(){ cout<<"x="<<x<<'\t'<<"AA\n"; }
};
class B{
public:
	int y;
	B(){y=300;}
	B(int x){y=x; }
	void Show(){cout<<"y="<<y<<'\t'<<"BB\n";}
};
class C:public A,public B{
public:
	int y;
	C(int a,int b,int c):A(a),B(b){y=c; }
	void Show(){ cout<<"y="<<y<<'\t'<<"cc\n"; }
};
int  main()
{  	C c1(400,500,600);
	c1.y=200;
	c1.Show();
	c1.A::Show();
	c1.B::Show();
	return 0;
}

4. 下列程序的输出结果是

#include<iostream>
using namespace std;
class base
{
public:
	void who(){ cout<<"base class"<<endl;}
};
class derivel:public base
{
public:
	void who(){cout<<"derivel class"<<endl;}
};
class derive2:public base
{
public:
	void who(){cout<<"derive2 class"<<endl; }
};
void fun(base *p)
{   p->who();}
int main()
{   base obj1,*p;
	derivel obj2;
	derive2 obj3;
	fun(&obj1);
	fun(&obj2);
	fun(&obj3);
	obj2.who();
	obj3.who();
	return 0;
}
base class
base class
base class
derivel class
derive2 class
程序解释:

base obj1,*p; 声明了基类 base 的一个对象 obj1 和一个指针 p。

derivel obj2; 声明了派生类 derivel 的一个对象 obj2。

derive2 obj3; 声明了派生类 derive2 的一个对象 obj3。

fun(&obj1); 调用函数 fun 传入 base 类的对象地址,函数中的 p->who(); 调用的是基类 base 的 who 方法。

fun(&obj2);fun(&obj3); 由于 fun 的参数类型是 base 类的指针,根据参数的静态类型,调用的也是 base 类的 who 方法。这里没有发生多态,因为 who 方法在基类中没有被声明为 virtual。

最后,obj2.who(); 和 obj3.who(); 直接调用其各自类的 who 方法,因为这里是通过对象调用,不涉及多态。

由于基类的 who() 方法没有被声明为虚函数 (virtual)fun() 函数中的 p->who() 调用不会展现多态行为(即不会根据对象的实际类型来调用对应的函数),而是会根据指针类型(基类 base)调用基类的 who() 方法。因此,前三次调用 fun() 都会输出 "base class"。而直接通过派生类对象调用 who() 时,则会调用相应派生类中的方法,分别输出 "derivel class""derive2 class"

三、编程题

1. 定义一个长方形 Rect 类,派生出长方体类 Cub, 计算派生类对象的表面积和体积。

#include <iostream>
using namespace std;
class Rect{
	public:
	double length;
	double width;
		Rect(double l,double w){
			length=l;width=w;
		}
		double area1(){
			return length*width;
		}
}; 
class Cub:public Rect{
	double height;
	public:
		Cub(double l,double w,double r):Rect(l,w){
		height=r;
		}
		double bs(){
			return 2*length*width+2*length*height+2*width*height;
		}
		double volume(){
			return area1()*height;
		}
};
int main(){
	Cub A(3,4,5);
	cout<<"表面积为:"<<A.bs()<<"体积为:"<<A.volume();
}
}

在这里插入图片描述

2. 定义一个 Shape 基类,并派生出圆球体(Sphere) 和立方体类(Cube), 分别求圆球体 与立方体对象的表面积和体积。

#include<iostream>
using namespace std;
class Shape {
protected: // protected允许子类访问
    double x, y;
public:
    Shape(double a, double b) {
    	x=a;y=b;
	}
    double area() {
        return 0;
    } 
    double volume() {
        return 0;
    }
};
class Sphere : public Shape {
    double radius;
public:
    Sphere(double d): Shape(d, d){
    	radius=d;
	} // 调用基类构造函数并初始化radius
    double area() {
        return 3.14 * radius * radius * 4;
    }
    double volume() {
        return 3.14 * radius * radius * radius * 4 / 3;
    }
};
class Cube : public Shape {
    double z;
public:
    Cube(double a, double b, double c) : Shape(a, b) {
    	z=c;
	} // 调用基类构造函数并初始化z
    double area() {
        return 2 * (x * y + x * z + y * z);
    }
    double volume() {
        return x * y * z;
    }
};
int main() {
    Sphere A(1);
    cout << "圆球体的表面积为:" << A.area() << "\n"; 
    cout << "圆球体的体积为:" << A.volume() << "\n"; 
    Cube B(3, 4, 5);
    cout << "立方体的表面积为:" << B.area() << "\n"; 
    cout << "立方体的体积为:" << B.volume() << "\n"; 
    return 0;
}

在这里插入图片描述

3. 定义一个点类(Point)、矩形类(Rectangle和立方体类(Cube)的层次结构。矩形包括长度和宽度两个新数据成员,矩形的位置从点类继承。立方体类由长度、宽度和高度构成要求各类提供支持初始化的构造函数和显示自己成员的成员函数。编写主函数,测试这个层次结构,输出立方体类的相关信息。

#include<iostream>
using namespace std;
class Point//描述二维平面中一个点的类
{
    double x, y;
public:
    Point(float i=0, float j=0) { x=i; y=j;}
    double area( ) { return 0.0;}
};
class Rectangle:public Point
{
	float length;
	protected:
		float width;
	public:
		Rectangle(){
			length=0;width=0;
		} 
		Rectangle(float l,float w){
			length=l;width=w;
		}
		float Area(){
			return length*width;
		}
		float GetL(){
			return length;
		}
		float GetW(){
			return width;
		}
		void RPrint(){
			cout<<"矩形的长为:"<<length;
			cout<<"\t宽为"<<width;
		}
};
class Cuboid:public Rectangle
{
	float high;
	public:
		Cuboid(){
			high=0;
		}
		Cuboid(float l,float w,float h):Rectangle(l,w){
			high=h;
		} 
		void CPrint()
		{
			cout<<"长方体的长为:"<<GetL()<<"\t宽为:"<<width;
			cout<<"\t高为:"<<high<<"\t体积为:"<<high*Area()<<'\n';
		}
};
int main(){
	Rectangle r(10,5);
	r.RPrint();
	cout<<"\t面积为:"<<r.Area()<<'\n';
	Cuboid c(6,3,10);
	cout<<"矩形的长为:"<<c.GetL()<<"\t宽为:"<<c.GetW();
	cout<<"\t面积为:"<<c.Area()<<'\n';
	c.CPrint(); 
}

在这里插入图片描述

文章来源:https://blog.csdn.net/weixin_74774974/article/details/135327041
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。