a=2, b=4
a=4, b=4
解释如下:
- 类
T
中定义了两个私有成员变量:一个非静态的int a
和一个静态的int b
。 - 在类
T
的外部,静态成员变量b
被初始化为2。 - 构造函数
T(int c)
接受一个整数参数c
,并初始化a
为c
的值,同时b
被乘以c
。 display
函数是一个静态成员函数,它接受一个T
类型的参数C
,并输出C.a
和静态成员变量b
的值。- 在
main
函数中,创建了两个T
类的对象A
和B
,分别使用构造函数初始化,传入参数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。
- 对于
- 调用
T::display(A)
输出A
的a
值(2)和当前的b
值(4)。 - 调用
T::display(B)
输出B
的a
值(4)和当前的b
值(仍然是4)。
因此,输出结果是a=2, b=4
和a=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! |
解释如下:
-
base
类定义了四个成员函数:f1()
,f2()
,f3()
, 和f4()
。其中f1()
,f2()
, 和f3()
是虚函数。 -
derive
类从base
类公有继承,并重写了f1()
和f4()
函数。它还定义了一个新的f2(int x)
函数,但注意这个新函数与base
类中的f2()
函数在参数列表上不同,因此它们不是重写关系,而是重载关系。 -
在
main()
函数中,创建了一个base
类的对象obj1
和一个derive
类的对象obj2
。还定义了一个指向base
类的指针ptr
。 -
ptr
首先指向obj1
。通过ptr
调用f1()
时,由于f1()
是虚函数,会调用derive
类中的f1()
(因为ptr
实际指向obj1
,但obj1
的类型是base
的派生类derive
,因此发生动态绑定)。所以输出f1 function of derive!
。 -
ptr->f2()
调用的是base
类的f2()
函数,因为derive
类中定义的f2(int x)
与此处的调用不匹配(参数列表不同)。所以输出f2 function of base!
。 -
ptr->f3()
也调用base
类的f3()
函数,因为derive
类没有重写f3()
。所以输出f3 function of base!
。 -
接下来,
ptr
指向obj2
。通过ptr
调用f1()
时,由于虚函数机制,会调用derive
类的f1()
。输出f1 function of derive!
。 -
ptr->f2()
调用会再次产生问题,因为ptr
现在是derive
类型的对象obj2
的地址,但是derive
类的f2(int x)
与此处没有参数的调用不匹配。同时,由于base
类的f2()
是虚函数,编译器会尝试查找derive
类中是否有匹配的虚函数重写,但没有找到。因此,正确的做法是调用base
类的f2()
,但这里的调用没有参数,而base
类的f2()
需要一个参数,这会导致编译错误。如果忽略这个编译错误,按照程序逻辑,应该调用base
类的f2()
。 -
ptr->f4()
调用的是derive
类的f4()
函数,因为f4()
不是虚函数,直接根据指针类型(base*
)进行静态绑定。输出f4 function of derive!
。
注意:实际运行时,ptr->f2()
会因为参数不匹配而导致编译错误。如果程序能够编译通过并运行到这一步,将调用 base
类的 f2()
函数,但由于没有传递参数,这可能会导致运行时错误或未定义行为。在实际编程中,应确保虚函数在基类和派生类中有匹配的签名。
读上述程序后,我们可以确定程序的输出结果。首先,我们需要理解类的继承关系和构造函数的调用顺序。
-
类
A
和B
是基类,它们分别有一个整数成员变量a
和b
,以及一个构造函数和一个disp
函数用于显示成员变量的值。 -
类
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
函数中:
- 创建了一个
Animal
类型的对象v
。 - 创建了一个
Felines
类型的对象a
。 - 创建了一个
Lion
类型的对象b
。 - 创建了一个
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 ; } |
这里的初始化列表表明:
- 首先调用
B
的构造函数,传递s1
和s2
。 - 然后调用
C
的构造函数,传递s1
和s3
。 - 接着调用
A
的构造函数,传递s1
。但由于B
和C
已经虚拟继承自A
,所以这里的A(s1)
实际上会被忽略,因为A
的构造函数已经在B
和C
的构造函数中通过虚拟继承被调用过了。 - 最后,执行
D
的构造函数体,输出s4
。
现在,我们按照上面的顺序来模拟程序的输出:
- 调用
B
的构造函数,输出s1
(即"class A")和s2
(即"class B")。 - 调用
C
的构造函数,输出s1
(即"class A")和s3
(即"class C")。 A
的构造函数由于虚拟继承的原因,这里不会被再次调用。- 执行
D
的构造函数体,输出s4
(即"class D")。
因此,程序的输出结果为:
复制代码
class A | |
class B | |
class A | |
class C | |
class D |
注意,由于A
的构造函数在虚拟继承中被调用了两次(一次通过B
,一次通过C
),所以"class A"被输出了两次。这是虚拟继承的一个特性,即共享的基类只会被构造一次,但在这种情况下,由于构造函数的输出,它看起来像是被调用了两次。