3,27程序阅读题

a=2, b=4

a=4, b=4

解释如下:

  1. T中定义了两个私有成员变量:一个非静态的int a和一个静态的int b
  2. 在类T的外部,静态成员变量b被初始化为2。
  3. 构造函数T(int c)接受一个整数参数c,并初始化ac的值,同时b被乘以c
  4. display函数是一个静态成员函数,它接受一个T类型的参数C,并输出C.a和静态成员变量b的值。
  5. main函数中,创建了两个T类的对象AB,分别使用构造函数初始化,传入参数2和4。
    • 对于A(2)a被初始化为2,b被初始化为2(因为在构造A之前b的值为2),然后b被乘以2,变成4。
    • 对于B(4)a被初始化为4,此时b的值为4(因为在构造B之前b的值已经被A的构造函数更新为4),然后b被乘以4,但因为b是静态变量,其值不会改变,仍然是4。
  6. 调用T::display(A)输出Aa值(2)和当前的b值(4)。
  7. 调用T::display(B)输出Ba值(4)和当前的b值(仍然是4)。

因此,输出结果是a=2, b=4a=4, b=4

总结:静态成员变量static int b只能被改变一次 所以第二次B(4)后 b的值不会改变

2. 阅读下面程序,写出输出结果

#include <iostream.h>

class base 

public:

    virtual void f1( ) { cout<<“f1 function of base! \n”; }

    virtual void f2( ) { cout<<“f2 function of base! \n”; }

    virtual void f3( ) { cout<<“f3 function of base! \n”; }

    void f4( ) { cout<<“f4 function of base! \n”; }

};

class derive: public base 

public:

void f1( ) { cout<<“f1 function of derive! \n”; }

void f2(int x) { cout<<“f2 function of derive! \n”; }

    void f4( ) { cout<<“f4 function of derive! \n”; }

};

void main( )

{   

base obj1,*ptr;

derive obj2;

ptr = &obj1;

    ptr->f1( );    ptr->f2( );    ptr->f3( );

    ptr = &obj2;

ptr->f1( ); ptr->f2( );    ptr->f4( );

}

阅读上述程序后,我们可以得出以下输出结果:

 
  

复制代码

f1 function of base!
f2 function of base!
f3 function of base!
f1 function of derive!
// 注意:这里会产生编译错误,因为 ptr->f2() 调用的是 base 类的 f2(),而 base 类的 f2() 没有参数
// 假设程序能够忽略这个错误继续执行(实际上这是不可能的),那么应该调用 base 类的 f2() 函数
f4 function of derive!

解释如下:

  1. base 类定义了四个成员函数:f1()f2()f3(), 和 f4()。其中 f1()f2(), 和 f3() 是虚函数。

  2. derive 类从 base 类公有继承,并重写了 f1() 和 f4() 函数。它还定义了一个新的 f2(int x) 函数,但注意这个新函数与 base 类中的 f2() 函数在参数列表上不同,因此它们不是重写关系,而是重载关系。

  3. 在 main() 函数中,创建了一个 base 类的对象 obj1 和一个 derive 类的对象 obj2。还定义了一个指向 base 类的指针 ptr

  4. ptr 首先指向 obj1。通过 ptr 调用 f1() 时,由于 f1() 是虚函数,会调用 derive 类中的 f1()因为 ptr 实际指向 obj1,但 obj1 的类型是 base 的派生类 derive,因此发生动态绑定)。所以输出 f1 function of derive!

  5. ptr->f2() 调用的是 base 类的 f2() 函数,因为 derive 类中定义的 f2(int x) 与此处的调用不匹配(参数列表不同)。所以输出 f2 function of base!

  6. ptr->f3() 也调用 base 类的 f3() 函数,因为 derive 类没有重写 f3()。所以输出 f3 function of base!

  7. 接下来,ptr 指向 obj2。通过 ptr 调用 f1() 时,由于虚函数机制,会调用 derive 类的 f1()。输出 f1 function of derive!

  8. ptr->f2() 调用会再次产生问题,因为 ptr 现在是 derive 类型的对象 obj2 的地址,但是 derive 类的 f2(int x) 与此处没有参数的调用不匹配。同时,由于 base 类的 f2() 是虚函数,编译器会尝试查找 derive 类中是否有匹配的虚函数重写,但没有找到。因此,正确的做法是调用 base 类的 f2(),但这里的调用没有参数,而 base 类的 f2() 需要一个参数,这会导致编译错误。如果忽略这个编译错误,按照程序逻辑,应该调用 base 类的 f2()

  9. ptr->f4() 调用的是 derive 类的 f4() 函数,因为 f4() 不是虚函数,直接根据指针类型(base*)进行静态绑定。输出 f4 function of derive!

注意:实际运行时,ptr->f2() 会因为参数不匹配而导致编译错误。如果程序能够编译通过并运行到这一步,将调用 base 类的 f2() 函数,但由于没有传递参数,这可能会导致运行时错误或未定义行为。在实际编程中,应确保虚函数在基类和派生类中有匹配的签名。

读上述程序后,我们可以确定程序的输出结果。首先,我们需要理解类的继承关系和构造函数的调用顺序。

  1. 类 A 和 B 是基类,它们分别有一个整数成员变量 a 和 b,以及一个构造函数和一个 disp 函数用于显示成员变量的值。

  2. 类 C 继承自 B 和 A,并且它有两个 A 类型的成员变量 ma1 和 ma2。类 C 的构造函数使用初始化列表来初始化基类 A 和 B 以及成员变量 ma1 和 ma2

在初始化列表中,成员变量的初始化顺序是它们在类中声明的顺序,而不是它们在初始化列表中的顺序。因此,即使 ma2 在 ma1 之前出现在初始化列表中,ma1 仍然会在 ma2 之前被初始化。

对于 C 类的构造函数 C(int k),初始化列表的顺序是:

  • A(k-2)(基类 A 的构造函数)
  • B(k+2)(基类 B 的构造函数)
  • ma2(k-1)(成员变量 ma2 的构造函数)
  • ma1(k+1)(成员变量 ma1 的构造函数)

现在,我们根据提供的 main 函数来确定程序的输出:

 
  

cpp复制代码

void main(){
C obj(10); // 调用 C 类的构造函数,参数 k 为 10
obj.disp(); // 调用 C 类的 disp 函数来显示成员变量的值
}

当 C obj(10); 被调用时,会发生以下事情:

  • 首先,基类 A 的构造函数被调用,a 被赋值为 10 - 2 = 8
  • 然后,基类 B 的构造函数被调用,b 被赋值为 10 + 2 = 12
  • 接着,成员变量 ma2 的构造函数被调用,它的基类 A 的 a 被赋值为 10 - 1 = 9
  • 最后,成员变量 ma1 的构造函数被调用,它的基类 A 的 a 被赋值为 10 + 1 = 11

现在,当 obj.disp(); 被调用时:

  • A::disp() 会显示 ma1 中的 a 的值,即 11
  • B::disp() 会显示 b 的值,即 12
  • 最后,输出 c 的值,即 10

因此,程序的输出结果为:

 
  

复制代码

A Constructor
B Constructor
A Constructor
A Constructor
C Constructor
a=11
b=12
c=10

 

5、阅读下面程序,写出输出结果

#include <iostream.h>

class Animal

{ public:  virtual void Display(){ cout<<"动物"<<endl; }

};

class Felines: public Animal

{ public:  void Display()  { cout<<"猫科动物"<<endl;  }

};

class Lion: public Felines

{ public: void Display()  { cout<<"狮子"<<endl; }

};

void fun(Animal *ptr) { ptr->Display();   }

void main()

{   Animal v;

Felines a;

Lion b;

Animal *p;

    p = &v;  fun(p); 

    p=&a; fun(p); 

    p=&b; fun(p); 

}

5。阅读程序后,我们可以确定输出结果。

首先,我们分析三个类及其成员函数:

  • Animal 类有一个虚函数 Display,它输出“动物”。
  • Felines 类继承自 Animal 类,并重写了 Display 函数,输出“猫科动物”。
  • Lion 类继承自 Felines 类,并进一步重写了 Display 函数,输出“狮子”。

接下来,我们分析 fun 函数和 main 函数:

  • fun 函数接受一个 Animal 类型的指针 ptr,并调用该指针指向对象的 Display 函数。由于 Animal 类的 Display 函数是虚函数,因此实际调用的将是 ptr 所指向的实际类型的 Display 函数(多态性)。

在 main 函数中:

  1. 创建了一个 Animal 类型的对象 v
  2. 创建了一个 Felines 类型的对象 a
  3. 创建了一个 Lion 类型的对象 b
  4. 创建了一个 Animal 类型的指针 p

然后,通过指针 p 分别指向了三个对象,并调用了 fun 函数:

  • p = &v; fun(p);:此时 p 指向 Animal 对象 v,因此调用的是 Animal 类的 Display 函数,输出“动物”。
  • p = &a; fun(p);:此时 p 指向 Felines 对象 a,但由于 Felines 类重写了 Display 函数,并且 Animal 类的 Display 函数是虚函数,因此调用的是 Felines 类的 Display 函数,输出“猫科动物”。
  • p = &b; fun(p);:此时 p 指向 Lion 对象 b,同样由于虚函数的多态性,调用的是 Lion 类的 Display 函数,输出“狮子”。

因此,程序的输出结果为:

 

复制代码

动物
猫科动物
狮子

6、阅读下面程序,写出输出结果

#include<iostream.h>

class A

{ public :

 A(const char *s) 

{ cout << s << endl ; }

    ~A(){}

};

class B : virtual public A

{ public : 

B(const char *s1, const char *s2) : A( s1 )  

{ cout << s2 << endl ; }

};

class C : virtual public A

{ public :

 C(const char *s1, const char *s2):A(s1)  

{ cout << s2 << endl ; }

};

class D : public B, public C

public :

D( const char *s1, const char *s2, const char *s3, const char *s4 )

: B( s1, s2 ), C( s1, s3 ), A( s1 )

{ cout << s4 << endl ; }

};

首先,我们需要理解类的继承关系和构造函数调用顺序。

D继承自类B和类C,而类B和类C都虚拟继承自类A。这意味着在创建D的实例时,类A的构造函数只会被调用一次。

接下来,我们看类D的构造函数初始化列表:

 
  

cpp复制代码

D( const char *s1, const char *s2, const char *s3, const char *s4 )
: B( s1, s2 ), C( s1, s3 ), A( s1 )
{ cout << s4 << endl ; }

这里的初始化列表表明:

  1. 首先调用B的构造函数,传递s1s2
  2. 然后调用C的构造函数,传递s1s3
  3. 接着调用A的构造函数,传递s1。但由于BC已经虚拟继承自A,所以这里的A(s1)实际上会被忽略,因为A的构造函数已经在BC的构造函数中通过虚拟继承被调用过了。
  4. 最后,执行D的构造函数体,输出s4

现在,我们按照上面的顺序来模拟程序的输出:

  1. 调用B的构造函数,输出s1(即"class A")和s2(即"class B")。
  2. 调用C的构造函数,输出s1(即"class A")和s3(即"class C")。
  3. A的构造函数由于虚拟继承的原因,这里不会被再次调用。
  4. 执行D的构造函数体,输出s4(即"class D")。

因此,程序的输出结果为:

 
  

复制代码

class A
class B
class A
class C
class D

注意,由于A的构造函数在虚拟继承中被调用了两次(一次通过B,一次通过C),所以"class A"被输出了两次。这是虚拟继承的一个特性,即共享的基类只会被构造一次,但在这种情况下,由于构造函数的输出,它看起来像是被调用了两次。

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
《java程序设计》课程的库资料,由贺州学院整理,可供学生期末课程复习使用,也可以供相关任课教师出卷使用。 内容示例为: 9. 阅读下列程序片段,写出运行结果。 public class Test { public static void main(String[] args) { int percent = 10; tripleValue(percent); System.out.println(percent); } public static void tripleValue(int x) { x = 3 * x; } } 代码执行后输出的结果是______。 答案:10 [解析] static 关键字应用的场合有:①用来修饰类中定义的变量,这样的变量称为类变量或静态变量。②可以用来修饰类中定义的方法,这样的方法称为静态方法。③用来修饰初始化语句块,这样的语句块常称为静态初始化语句块。static 在这里表示这个方法为类方法,不属于任何对象实例,而是类所有,描述对象的共有动作,可以用类名直接调用。在调用了tripleValue函数之后,函数的值没有返回,所以percent的值还是10。 10. 阅读下列程序片段,写出运行结果。 class Shape { public Shape() { System.out.print("Shape"); } } class Circle extends Shape { public Circle() { System.out.print("Circle"); } } public class Test { public static void main(String[] args) { Shape d = new Circle(); } } 代码执行后输出的结果是______。 答案:ShapeCircle [解析] 继承是而向对象编程的一个主要优点之一,它对如何设计Java类有着直接的影响。继承有如下几点好处: ①它可以利用已有的类来创建自己的类,只需要指出自己的类和已有的其他类有什么不同即可,而且还可以动态访问其他有 关类中的信息。 ②通过继承,可以利用Java类库所提供的丰富而有用的类,这些类都已经被很好地实现。 ③当设计很大的程序时,继承可以使程序组织得层次清晰,有利于程序设计相减少错误的发生。该程序首先编写了一个Shape的类,然后又编写一个类Circle去继承Shape类。由于子类拥有父类所有的属性和方法,所以输出的是ShappeCircle。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值