二、编程题选择类
1.下面代码输出结果:( D)
class A { public: void f(){ cout<<"A::f()"<<endl; } int a; }; class B : public A { public: void f(int a){cout<<"B::f()"<<endl;} int a; }; int main() { B b; b.f(); return 0; }
A.打印A::f()
B.打印B::f()
C.不能通过编译,因为基类和派生类中a的类型以及名称完全相同
D.以上说法都不对
首先看main函数,定义了一个对象b,既然是对象先去B类中找f()函数如果没有才去父类中寻找,而子类中由于f()函数与父类函数同名构成隐藏,隐藏了父类的函数实现方法所以只能调用子类的f(int a)函数,而由于缺乏参数所以编译报错。
2.下面哪项结果是正确的(C )
class Base1 { public: int _b1; }; class Base2 { public: int _b2; }; class Derive : public Base1, public Base2 { public: int _d; }; int main(){ Derive d; Base1* p1 = &d; Base2* p2 = &d; Derive* p3 = &d; return 0; }
A.p1 == p2 == p3
B.p1 < p2 < p3
C.p1 == p3 != p2
D.p1 != p2 != p3
首先d继承了base1和base2,有一个base1的指针存储d对象,base2的指针也存储d对象,派生类指针存储子类对象,下面我们画图看看他们的关系:
由于d先继承的base1所以只有base1的首地址与d相同,而base2的首地址在base1类的下一个所以和d不相同。
3.下列代码中f函数执行结束后输出(C )
class A { public: A() { cout<<"A::A()"<<endl; } ~A() { cout<<"A::~A()"<<endl; } int a; }; class B : public A { public: B() { cout<<"B::B()"<<endl; } ~B() {cout<<"B::~B()"<<endl; } int b; }; void f() { B b; }
A.B::B() B::~B()
B.B::B() A::A() A::~A() B::B()
C.A::A() B::B() B::~B() A::~A()
D.以上都不对
首先从f()函数开始看起,有一个子类对象b,这时候调用B的构造函数,进入B的构造函数初始化列表调用父类A的构造函数,所以先打印A(),然后进入B构造函数的函数体打印B(),然后函数结束开始析构,先析构子类对象打印~B(),然后子类析构结束后自动调用父类析构~A()。
4.以下哪项说法时正确的(D )
class A { public: void f1(){cout<<"A::f1()"<<endl;} virtual void f2(){cout<<"A::f2()"<<endl;} virtual void f3(){cout<<"A::f3()"<<endl;} }; class B : public A { public: virtual void f1(){cout<<"B::f1()"<<endl;} virtual void f2(){cout<<"B::f2()"<<endl;} void f3(){cout<<"B::f3()"<<endl;} };
A.基类和子类的f1函数构成重写
B.基类和子类的f3函数没有构成重写,因为子类f3前没有增加virtual关键字
C.如果基类指针引用子类对象后,通过基类对象调用f2时,调用的是子类的f2
D.f2和f3都是重写,f1是重定义
f1由于在基类中没有加virtual关键字,所以只能构成隐藏。f2和f3满足重写的条件。
5.以下程序输出结果是( C)
class A { public: A ():m_iVal(0){test();} virtual void func() { std::cout<<m_iVal<<‘ ’;} void test(){func();} public: int m_iVal; }; class B : public A { public: B(){test();} virtual void func() { ++m_iVal; std::cout<<m_iVal<<‘ ’; } }; int main(int argc ,char* argv[]) { A*p = new B; p->test(); return 0; }
A.1 0
B.0 1
C.0 1 2
D.2 1 0
E.不可预期
F. 以上都不对
首先从main函数看起,父类指针存放子类是多态的信号,先自动调用B类的构造函数,在B的构造函数的初始化列表调用A的构造函数,然后将mval初始化为0,调用A类中的test函数,在test函数中又调用了func函数,这个时候由于派生类的构造函数初始化列表还没走完,所以没有虚表指针不构成多态,只能调用A类中的func打印0,然后进入B类的构造函数的函数体中调用test函数,由于B中无test函数只能去父类A中调用,在A类中的test函数体中调用func函数,这个时候因为派生类的初始化列表已经走完了虚表指针形成了,并且func被子类重写由this指针也就是A*父类指针调用func满足多态所以在B类中的func中先让mval++变成1然后打印1,接下来由父类指针P主动调用test函数,同样满足多态调用B类中的func函数,mval++变成2然后打印2,所以答案是0 1 2.
6.下面函数输出结果是( A)
class A { public: virtual void f() { cout<<"A::f()"<<endl; } }; class B : public A { private: virtual void f() { cout<<"B::f()"<<endl; } }; int main() { A* pa = (A*)new B; pa->f(); }
A.B::f()
B.A::f(),因为子类的f()函数是私有的
C.A::f(),因为强制类型转化后,生成一个基类的临时对象,pa实际指向的是一个基类的临时对象
D.编译错误,私有的成员函数不能在类外调用
先从main函数看起,父类指针存放子类,先调用子类的构造函数,无构造我们就直接往下讲了,由于继承中天生的赋值类型转换,所以子类到父类并不需要强转,所以这一步没有作用,父类指针调用f函数,进入A类中发现f是虚函数子类重写了这个虚函数所以调用B类中的f()函数,虽然这个时候B类中的f()函数是私有的,但是多态仅仅是用子类函数的地址覆盖虚表,最终调用的位置不变只是执行函数发生了变化,所以还是打印B()
7.下面 C++ 程序的运行结果是(C)
class parent { int i; protected: int x; public: parent() { x = 0; i = 0; } void change() { x++; i++; } void display(); }; class son :public parent { public: void modify(); }; void parent::display() { cout << "x=" << x << endl; } void son::modify() { x++; } int main() { son A; parent B; A.display(); A.change(); A.modify(); A.display(); B.change(); B.display(); return 0; }
A:x=1 x=0 x=2
B :x=2 x=0 x=1
C :x=0 x=2 x=1
D: x=0 x=1 x=2
先进入main函数,有一个子类对象A,有一个父类对象B,都经过构造函数初始化x=0,A调用A中的display打印x = 0,然后A调用父类中的change函数(因为子类无change函数),x变成1,然后A调用A中的modify函数x++变成2然后打印2,由于A和B是两个不同的类,所以B调用B中的change函数后x从0变成1,然后打印1。
8.分析一下这段程序的输出(A)
class B { public: B() { cout << "default constructor" << " "; } ~ B() { cout << "destructed" << " "; } B(int i): data(i) { cout << "constructed by parameter" << data << " "; } private: int data; }; B Play( B b) { return b; } int main(int argc, char *argv[]) { B temp = Play(5); return 0; }
A constructed by parameter5 destructed destructed
B constructed by parameter5 destructed
C default constructor" constructed by parameter5 destructed
D default constructor" constructed by parameter5 destructed destructed
首先进入main函数,调用play函数,而play函数的参数是B类对象,所以这里生成一个B类的临时对象将5拿来构造B然后调用B的构造函数打印constructed by parameter 5,然后调用拷贝构造将这个对象给play函数中的形参,返回的时候本来要创建一个B类的临时对象调用拷贝构造将b给临时对象后再释放原来形参中的那个b变量,但是由于编译器的优化会直接将play的返回值给temp,然后析构掉刚刚那个返回值对象,所以返回值析构打印destructed,函数结束后temp对象析构打印destructed.
9.以下程序的输出是(C)
class Base { public: Base(int j): i(j) {} virtual~Base() {} void func1() { i *= 10; func2(); } int getValue() { return i; } protected: virtual void func2() { i++; } protected: int i; }; class Child: public Base { public: Child(int j): Base(j) {} void func1() { i *= 100; func2(); } protected: void func2() { i += 2; } }; int main() { Base * pb = new Child(1); pb->func1(); cout << pb->getValue() << endl; delete pb; }
A 11
B 101
C 12
D 102
首先基类指针存放子类对象是多态的信号,child调用自己的构造函数,然后在初始化列表中用1初始化基类对象,然后去基类的构造函数中用1初始化i,然后调用func1函数,由于func1不是虚函数所以调用基类的func1i*10==10,然后调用func2函数,发现func2是虚函数并且this指针是父类指针所以调用子类中的func2函数i变成12.
10.下面 C++ 代码的运行结果为(A)
class B0 { public: virtual void display() { cout << "B0::display0" << endl; } }; class B1 :public B0 { public: void display() { cout << "B1::display0" << endl; } }; class D1 : public B1 { public: void display() { cout << "D1::display0" << endl; } }; void fun(B0 ptr) { ptr.display(); } int main() { B0 b0; B1 b1; D1 d1; fun(b0); fun(b1); fun(d1); }
A B0::display0 B0::display0 B0::display0
B B0::display0 B0::display0 D1::display0
C B0::display0 B1::display0 D1::display0
D B0::display0 B1::display0 B1::display0
首先main函数有3个对象,然后都去调用fun函数,而fun函数参数是基类对象,由对象调用display只能调用父类B0自己的函数,所以打印三次B0::display0
11.下面 C++ 程序的运行结果为(A)
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; } }; int main() { D* p = new D("class A", "class B", "class C", "class D"); delete p; return 0; }
A class A class B class C class D
B class D class B class C class A
C class D class C class B class A
D class A class C class B class D
首先这是个多继承问题,我们可以看到D类先继承B,再继承C,而B类中先继承了A,所以D中构造函数的初始化顺序为A B C D,A用s1初始化进入s1的构造函数打印class A,然后用s1和s2初始化B,由于B虚继承A只有一份A所以刚开始在A初始化一次后后面在其他类的初始化列表就不再初始化A了,所以打印S2也就是classB,然后用s1和s3初始化C,与B同理A不初始化打印S3也就是class C,最后进入D的构造函数函数体打印classD
12.有如下C++代码(A)
struct A{ void foo(){printf("foo");} virtual void bar(){printf("bar");} A(){bar();} }; struct B:A{ void foo(){printf("b_foo");} void bar(){printf("b_bar");} }; A *p = new B; p->foo(); p->bar();
A barfoob_bar
B foobarb_bar
C barfoob_foo
D foobarb_fpp
首先A和B是struct,struct默认访问权限和继承权限都是公有,new一个B对象,调用B的构造函数在B的构造函数的初始化列表调用A的构造函数,在A的构造函数中调用bar函数,由于初始化列表没有结束只能调用A的bar函数所以打印bar,然后父类指针调用foo函数foo不是虚函数所以调用A中的foo打印foo,然后父类指针调用bar函数,bar是虚函数满足多态条件所以打印b_bar.
13.以下程序输出结果是(B)
class A { public: virtual void func(int val = 1) { std::cout<<"A->"<<val <<std::endl;} virtual void test() { func();} }; class B : public A { public: void func(int val=0) {std::cout<<"B->"<<val <<std::endl;} }; int main(int argc ,char* argv[]) { B*p = new B; p->test(); return 0; }
A :A->0
B :B->1
C :A->1
D: B->0
首先子类指针存放子类对象,然后调用test函数,进入B类中发现没有test函数只能去父类中找,然后调用调用父类中的test函数,函数体中调用func函数,这时的this指针为A*,func为虚函数去B类中调用func函数,由于缺省参数是编译期间就完成的,而虚函数是运行期间完成的,所以当调用func函数的时候缺省参数还是父类的int val = 1,所以这时候打印B->1.
14.下面程序的输出是(B)
class A { public: void foo() { printf("1"); } virtual void fun() { printf("2"); } }; class B: public A { public: void foo() { printf("3"); } void fun() { printf("4"); } }; int main(void) { A a; B b; A *p = &a; p->foo(); p->fun(); p = &b; p->foo(); p->fun(); A *ptr = (A *)&b; ptr->foo(); ptr->fun(); return 0; }
A 121434
B 121414
C 121232
D 123434
首先父类指针p存放父类对象,所以调用的函数都是父类的,打印1 2,然后p又存放子类对象,由于fun是虚函数所以打印1 4,因为子类对象本来就可以直接给父类,所以强转无任何意义与p = &b一模一样,还是打印1 4.
15.下面这段代码运行时会出现什么问题(B)
class A { public: void f() { printf("A\n"); } }; class B: public A { public: virtual void f() { printf("B\n"); } }; int main() { A *a = new B; a->f(); delete a; return 0; }
A 没有问题,输出B
B 不符合预期的输出A
C 程序不正确
D 以上答案都不正确
因为父类指针保存的子类对象,f不是虚函数正常调用A的f函数,但是delete的时候只会调用父类的析构函数这里是会崩溃的,正确的做法是将父类的析构函数定义为虚函数,就可以正常释放子类的资源了。