2005年自考面向对象程序设计模拟试题(二)

来源: 作者: 时间:2007-12-02 点击:


  三,改错题(本大题共5小题,每小题2分,共10分)

  21.分析下列程序中的错误,并说明出错原因。

    # include

    class base {

    const int n;

    public:

    base() { cout<<”Initializing default\n”; }

    base(int m) { cout<<”Initializing\n”; n=m; }

    ~base() { cout<<”Destroying\n”; }

    };

    void main()

    { base x=1;

    base y=x;

    }

       答:(1) n=m; const数据成员不以该形式赋值

    (2)错误处base y=x;,以对象初始化对象

  [解析](1)作为const数据成员n不能用赋值表达式的形式为其赋初值,必须在定义构造函数时在函数头中以“base(int m):n(m)”形式来初始化。

    (2)base y=x; 语句中‘=’不是赋值的意思,而是初始化的意思。即在定义类base的对象y时,以对象x给它初始化,相当于base y(x);。但是以对象初始化对象时,必须调用拷贝构造函数才行。因程序中没给出拷贝构造函数,也不能调用系统的缺省拷贝构造函数,因用户给出了构造函数后,再也不能调用系统提供的任何缺省构造函数。所以,base y=x;是错误的。

  22.分析下列程序中的错误,并说明出错原因。

    #include

    class AA {

    public: int aa;

    };

    class BB:virtual public AA {

    int bb;

    };

    class CC:virtual public AA {

    public: int bb;

    };

    class DD:public BB,public CC {

    public: float dd;

    };

    void main()

    { DD p;

    int m=p.aa;

    AA *pr=&p;

    AA &pt=p;

    DD *pp=(DD *)(BB *)pr;

    AA r;

    DD &ps=r;

    }

       答:(1)DD *pp=(DD *)(BB *)pr;,虚基类指针不能赋给派生类指针

    (2)DD &ps=r;,虚基类对象不能初始化派生类对象引用

  [解析]赋值兼容规则的三条规则是:公有派生的情况下,派生类的对象可以赋给基类的对象;派生类的对象可以初始化基类对象的引用;派生类对象地址可以赋给指向基类对象的指针。反过来,以上三条规则是错误的。

    (1)将虚基类AA的指针pr赋给派生类DD的指针pp,这违背了赋值兼容规则。

    (2)虚基类AA的对象r初始化派生类DD的引用ps,这违背了赋值兼容规则。

  23.分析下列程序中的错误,并说明出错原因。

    #include

    class A {

    int x;

    public:

    A(int a) { x=a; fun(); }

    virtual void fun()=0;

    };

    class B:public A {

    public:

    B(int b) :A(b) {}

    void fun() { }

    };

    void main()

    { A aa(5);

    B bb(8);

    }

       答:(1)fun();,构造函数不能调用纯虚函数

    (2)A aa(5); 抽象类不能定义对象

  [解析](1)在构造函数“A(int a)”调用了纯虚函数“fun()”,因构造函数调用虚函数是静态联编,即调用的是它自己类的虚函数。在此调用的是纯虚函数,它只被说明,而未定义,所以不可能被执行,因此出错。

    (2)抽象类相当于是对类的引用性声明,所以它只能定义该类的指针和引用,而不能定义它的对象。在此,A为抽象类,而“A aa(5)”却定义了A的对象,因此是错误的。

  24.分析下列程序中的错误,并说明出错原因。

    #include

    void fun()

    { ifstream myfile(“d:\abcd”);

    myfile<<”my file\n”;

    }

    void main()

    { fun(); }

       答:(1)ifstream ,输出必须用输出流ofstream

    (2)myfile(“d:\abcd”);,必须用‘\’

  [解析](1)由函数“fun()”的函数体可见,它要输出信息“my file”,为此必须让文件与输出流相关联,但“ifstream”是输入流,与之关联的文件只能输入信息。所以,必须使用输出流“ofstream”。

    (2)作为文件面的“d:\abcd”是字符串,其中字符‘\’一般作为转义符使用。为了在字符串中使用该字符的原义必须用双字符,即“\”。

  25.分析下列程序中的错误,并说明出错原因。

    #include

    class AA {

    int aa;

    AA(int xx) { aa=xx; }

    public:

    int get() { return aa; }

    ~AA() { cout<<”Destroying”<

    };

    main()

    { AA elem[3]={5,8,4};

    for (*int i=0;i<3;i++) cout<

    }

       答:(1)AA(int xx) 的访问属性错、应具有公有属性

    (2)数组elem[3]初始化错、因调用构造函数

  [解析](1)构造函数虽是由系统自动调用来生成对象的,但一般都是在类外调用,所以它的访问属性必须是公有的。程序中构造函数的访问属性是私有的(缺省值),运行中必定出错。

    (2)对于数组元素的初始化,当程序中提供了带参构造函数时,一般不能直接用数值表的形式来实现(如程序中所示),对只含有一个参数的构造函数,初始化数组的实现方式如下所示:AA elem[3]={ AA(5),AA(8),AA(4)};

  四,完成程序题(本大题共5小题,每小题4分,共20分)

    根据题目要求,完成程序填空。

  26.仔细阅读下列求两个点之间距离的程序,根据程序的输出结果在划线处填入正确语句。

    #include

    #include

    class point {

    float x,y;

    public:

    point(float a,float b) { x=a; y=b; }

    float distance(point &p) {

    float dx=_____①______;

    float dy=_____②______;

    return (float)srrt(da*da+dy*dy);

    }

    };

    void main()

    { point p1(2,3),p2(32,43);

    cout<<_____③_____<

    }

    输出结果:50

       答:① p.x-x

    ② p.y-y

    ③ distance(p2)

  [解析]由于求两点之间距离的函数“distance”为成员函数,所以由该函数的this指针所指对象可用作起点(参数之一),另一个用作终点的参数,必须在函数的参数表中显式地给出。dx 和dy分别计算两个结点间x和y坐标间的距离,因此应该为:dx=p.x – x 和 dy =p.y – y。对它们求平方相加后开方即可,得到两点间的距离:(float)srrt(da*da+dy*dy)。在主程序中求p1和p2两个结点之间的距离,由于由p1调用距离函数,所以由p2作为它的参数,即distance(p2)。

  27.完成如下的程序,使得输出为:

    base::10

    base::12

    derived::24

    #include

    class base {

    int x;

    public:

    base(int a) { x=a; }

    _________①__________;

    };

    class derived:public base {

    int y;

    public:

    derived(int a,int b):base(a) { y=b; }

    _________②_________;

    };

    void main()

    { base b(10),*p;

    derived d(12,24);

    b.print();

    p=&d;

    p->print();

    }

       答:① virtual void print() { cout<<”base::”<

    ② viod print() { base::print(); cout<<”derived::”<

  [解析]从主程序可见,“b.print();”是由基类的对象调用它的成员函数,要输出的是“base::10”,意味着基类中必须定义一个成员函数“print()”,该函数手它必须有输出语句“cout<<”base::”<print();”可知,派生类对象的地址初始化基类指针p,而由该指针来调用函数“print()”,意味着它调用的是派生类的函数。由输出“”base::12”和“derived::24”可知,派生类除了要输出自己的信息外,还要输出基类的信息,即“base::print(); cout<<”derived::”<

  28.下面是一个菜单程序,请在空格处填上正确的语句,使之能正常地工作。

    #include

    class Menu {

    public: virtual void fun()=0;

    };

    class Menu1:public Menu {

    public: void fun() { cout<<”do something for Menu1 !\n”; }

    };

    class Menu2:public Menu {

    public: void fun() { cout<<”do something for Menu2 !\n”; }

    };

    class Menu3:public Menu {

    public: void fun() { cout<<”do something for Menu3 !\n”; }

    };

    void main()

    { Menu *pm[3];

    pm[0]= new Menu1;

    pm[1]=new Menu2;

    pm[2]=new Menu3;

    int num;

    do {

    cout<<”1—Menu1 \n”;

    cout<<”2—Menu2 \n”;

    cout<<”3—Menu3 \n”;

    cout<<”enter your choose !\n”;

    cin>>num;

    if(______①______)

    _______②_______fun();

    } while(_____③_____);

    }

       答:① num>=1&&num<=3

    ② pm[num-1].

    ③ num!=0

  [解析]这个程序要完成的是菜单功能。在基类中定义了一个纯虚函数“fun()”,在下面的三个派生类中,各自分别对函数“fun()”进行了重新定义,以便完成各自的功能。因为只有三个菜单项,所以只在num取值范围在1和3之间时,才能执行所需功能,故在①处填入“num>=1&&num<=3”。当num取值范围在1和3之间时,分别执行菜单Menu1~Menu3的功能,也即执行派生类中相应函数“fun()”,为此要由指向各个派生类对象的基类指针来完成。由主程序可见,基类指针数组的各元素分别被赋给了各派生类的对象地址,考虑到C++数组的下标从0开始,②处填入“pm[num-1].”是正确的。为了使菜单循环工作,只有当输入0时才退出循环,③处应填入“num!=0”。

  29.两个复数只有当它们的实部和虚部分别相等时,才被认为它们相等。在空格处填入合适的内容,以完成下面的程序,使其重载运算符“==”,用以比较两个复数的相等。请在主函数中输出比较的结果。

    #include

    class complex {

    double real,imag;

    public:

    complex(double r,double i) { real=r; imag=i; }

    bool operator==(complex &);

    };

    int complex:: operator==(complex &com)

    { return(_____①_____) }

    void main()

    { complex c1(12.3,32.5),c2(21.7,18.6);

    if(______②______)

    return cout<<”true\n”;

    else

    return cout<<”false\n”;

    }

       答:① (real==com.real)&&(imag==com.imag)

    ② c1==c2或c1.operator==(c2)

  [解析]若两个复数的相等,则必有它们的实数部分和虚数部分都相等,所以运算符重载函数中返回“(real==com.real)&&(imag==com.imag)”,只有real==com.real与imag==com.imag都为真时,该函数的返回值才为真。在主程序中,为了比较两个复数c1和c2,可以隐式地写成“c1==c2”,也可显式写成“c1.operator==(c2)”。

  30.下列程序中声明了两个类AA和BB,其中函数“print”是类AA的成员函数,但是类BB的友元函数。请在①、②和③处各填入正确的内容,使程序能正常运行。

    #include

    _____①______;

    class AA {

    int t;

    public:

    AA(int x) { t=x; }

    void print(BB &b);

    };

    class BB {

    int s;

    public:

    BB(int y) { s=y; }

    friend void ___②___print(BB &);

    };

    void ____③_____

    { cout<<”AA:”<

    void main()

    { AA m(6);

    BB n(8);

    m.print(n);

    }

    输出结果为:AA:6;BB:8

       答:① class BB;

    ② AA::

    ③ AA::print(BB &w)

  [解析]由于AA类的成员函数“print”是类BB的友元函数,因此它必须有一个BB类的引用作为参数,以便有引用BB类数据成员的接口。但此时BB类还未定义,为解决此矛盾,在①处先对BB类作引用性声明“class BB;”,告诉系统BB类在后面定义。因为函数“print”是类AA的成员函数,在类外定义必须加上类名和作用域分辨符,即在②处加上“AA::”。在BB类外定义“print”时,因是友元,所以没有加上“BB::”的必要,但“AA::”还是必须加的,所以在③处填“AA::print(BB &w)”。

 五,程序分析题(本大题共6小题,每小题5分,共30分)

    给出下面各程序的执行结果。

  31.请分析以下的程序,给出该程序的正确执行结果。

    #include

    int add(int x,int y) { return x+y; }

    void main()

     { int m=2,n=3;

    cout<<”1:”<

    m=2,n=3;

    cout<<”2:”<

    m=2,n=3;

    cout<<”3:”<

    m=2,n=3;

    cout<<”4:”<

    }

       答:1:7

    2:8

    3:8

    4:9

  [解析]在说明答案之前,要说明两个问题:

    (1)C++语言中,函数参数是压在栈里的,因压栈是从前往后进行的,所以出栈就是从后向前进行的,也即先取最后的参数,然后再向前逐个取用;

    (2)对于单加运算,m++是先执行后加1,++m是加1后再执行。

    由此,因m=2,n=3;,所以:

    1:(m++)+(m+n)=2+(2+3)=7 (m++后执行,且后加1,所以m=2一直未变)

    2:(++m)+(m+n)=3+(2+3)=8 (++m后执行,但先加1,执行++m时,m=3了)

    3:(m+n)+( m++)=(3+3)+2=8 (先执行m++,后加1,执行m+n时,m=3了)

    4:(m+n)+(++m)=(3+3)+3=9; (先执行++m,且先加1,故一开始就有m=3)

  32.请分析下面的程序并给出该程序的执行结果。

    #include

    class AA {

    int a;

    public:

    AA() { cout<<”Initualizing AA!\n”; }

    ~AA() { cout<<”Destroying AA!\n”;

    };

    class BB {

    int b;

    AA p;

    public:

    BB() { cout<<”Initualizing BB!\n”; }

    ~BB() { cout<<”Destroying BB!\n”;

    };

    void main()

    { BB X;

    cout<<”Ending main!\n”;

    }

       答:Initualizing AA!

    Initualizing BB!

    Ending main!

    Destroying BB!

    Destroying AA!

  [解析]虽然在主程序中只定义了一个类BB的对象,但在类BB中声明了类AA的对象作为它的数据成员。当一个类中含有对象作为数据成员时,在生成对象时,先调用成员对象的构造函数,再调用类自己的构造函数,所以输出了“Initualizing AA!”(成员对象构造函数的输出)和“Initualizing BB!”(类自己构造函数的输出)。对象生成后,执行下一句输出语句,则输出“Ending main!”。此时程序结束,调用析构函数来析构掉对象,先调用类自身的析构函数,其输出为“Destroying BB!”,再调用成员对象的析构函数,其输出为“Destroying AA!”。

  33.写出下列程序的运行结果。

    #include

    class AA {

    int a;

    public:

    AA(int i) { a=i; cout<<”AA=”<

    virtual ~AA() { cout<<”~AA=”<

    };

    class BB:public AA {

    int b;

    public:

    BB(int i,int j):AA(i) { b=j; cout<<”BB=”<

    ~BB() { cout<<”~BB=”<

    };

    void main()

    { AA *pa=new AA(8);

    delete pa;

    AA *pb=new BB(6,9);

    delete pb;

    }

       答:AA=8

    ~AA=8

    AA=6

    BB=9

    ~BB=9

    ~AA=6

  [解析]语句“AA *pa=new AA(8);”动态生成一个类AA的对象并把它的地址赋给对象指针“pa”,为此系统调用了AA的构造函数,输出AA=8。接下来,执行语句“delete pa;”删除该对象指针,其实是删除了动态生成的对象,因此调用类AA的析构函数,输出~AA=8。接着执行语句“AA *pb=new BB(6,9);”,动态生成一个类BB的对象,并将其地址赋给类AA的指针“pb”。在生成派生类BB对象时,系统首先调用基类AA的构造函数输出AA=6,然后调用派生类BB的构造函数输出BB=9。最后执行语句“delete pb;”,由于基类AA的析构函数被定义为虚函数,因此在运行时是动态联编的。故系统先调用派生类BB的析构函数输出~BB=9,再调用基类AA的析构函数输出~AA=6(若基类析构函数非虚函数,则只输出~AA=6)。

  34.写出下列程序的运行结果。

    #include

    class AA {

    public:

    static int n;

    AA() { n++; }

    };

    int AA::n=0;

    main()

    { cout<<”AA::n=”<

    AA d1;

    cout<

    AA d2;

    cout<

    AA d3,d4;

    cout<

    cout<

    }

       答:AA::n=0

    d1.n=1

    d2.n=2

    d1.n=4

    d2.n=4

  [解析]由于数据成员n的访问属性是公有的,所以在类外可访问它;又它是静态变量,所以具有全局性。在构造函数中,执行的是n++操作,即每次调用构造函数n就加1。当程序开始时,因未定义对象,所以n的值为初始化时的值,则输出为“AA::n=0”。当定义了对象d1后,由于调用了构造函数,则该对象中n=1,故输出“d1.n=1”。同理,对象d2输出“d2.n=2”。由于接下来生成了两个对象d3和d4,调用两次构造函数,n两次加1,此时n=4,所以下面两条语句的输出为“d1.n=4”和“d2.n=4”。

  35.写出下列程序的输出结果。

    #include

    class AA {

    public:

    AA{} { cout<<”Constructor of AA. \n”; fun(); }

    virtual void fun() { cout<<”AA::fun() calle

    D.\n”; }

    };

    class BB:public AA {

    public:

    BB(){ cout<<”Constructor of B

    B.\n”; fun(); }

    void fun() { cout<<”BB::fun() calle

    D.\n”; }

    };

    void main()

    { BB d; }

       答:Constructor of AA.

    AA::fun() called.

     Constructor of BB.

    BB::fun() called.

  [解析]虽然函数fun()说明为虚函数,但当在构造函数中被调用时,呈现的是静态联编,即基类和派生类都调用各自的函数fun()。所以,当生成对象d时,先调用基类的构造函数,在构造函数中又调用自己的函数“fun()”,所以输出为“Constructor of AA.”和“AA::fun() called.”。同理调用派生类的构造函数时,生成的输出为“Constructor of BB.”和“BB::fun() called.”。

  36.请给出下面的程序的输出结果。

    #include

    template

    void sort(T* a,int n)

    { T num;

    for(int i=0;i

    { for(int j=i;j

    if(a[j]>a[j+1])

    { num=a[j]; a[j]=a[j+1]; a[j+1]=num; }

    }

    for(i=0;i

    cout<

    cout<

    }

    void main()

    { int iver[5]={ 12,45,9,23,37 };

    double dver[5]= { 22.3,56.7,43.5,13.8,8.6 };

    sort(iver,5);

    sort(dver,.5);

    }

       答:9 12 23 37 45

    8.6 13.8 22.3 43.5 56.7

  [解析]这是使用模板的例子。函数sort是一个递增的排序函数,T是个模板。当数组是整型数据类型时,它为整型数组排序;当数组为双精度数据类型时,它为双精度数组排序。所以输出如上结果。


     [收藏] [推荐] [评论] [打印] [关闭]
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册