面向对象程序设计问答题OOP1-003


1. 用代码说明在标准C++中如何进行输入输出,并解释各语句的含义是什么?

#include <iostream>
using namespace std;

int a,b;

int main()
{
    cin >> a >> b;
    cout << "hello world" << endl;
    return 0;
}
  • 输入时,从键盘输入的数据先放在键盘缓冲区中,当按回车键时,键盘缓冲区中的数据输入到程序中的输入缓冲区,形成$cin$流,然后用流提取运算符>>输入缓冲区中提取数据送给程序中的有关变量

  • 当用$cout$和流插入运算符<<向显示器输出数据时,先将这些数据送到程序中的输出缓冲区保存,直到缓冲区满了或遇到$endl$,就将缓冲区中的全部数据送到显示器显示出来。

2. 阐述C++中函数三种调用方式的实现机制、特点及其实参、形参格式,最好用代码说明。试比较三种方式的执行效率。(提示:传址、传值、引用传递)

  • 传值: 实参用变量名(或常数),形参用变量名

传值调用: 是单向的,实参把值拷贝一份给形参,形参值的改变不会影响实参

代码
#include <iostream>
using namespace std;

void swap(int a,int b)
{
    int temp;
    temp=a,a=b,b=temp;
    cout << "a=" << a << " b=" << b << endl;
}

int main()
{
    int i,j;
    cin >> i >> j;
    swap(i,j);
    cout << "i=" << i << " j=" << j << endl; 

    return 0;
}
运行结果

运行时输入2 5

a=5 b=2
i=2 j=5
  • 传指针: 实参用地址,形参用指针

传址调用:是双向的,把实参的地址传给形参,形参就指向实参变量,因此形参的改变会影响实参

代码
#include <iostream>
using namespace std;

void swap(int *a,int *b)
{
    int temp;
    temp=*a,*a=*b,*b=temp;
    cout << "a=" << *a << " b=" << *b << endl;
}

int main()
{
    int i,j;
    cin >> i >> j;
    swap(&i,&j);
    cout << "i=" << i << " j=" << j << endl; 

    return 0;
}
运行结果

运行时输入2 5

a=5 b=2
i=5 j=2
  • 传引用: 实参用变量名,形参用引用

引用调用:形参是实参的别名,形参的改变也会影响实参,注意,实参只能用变量名,不能用常量

代码
#include <iostream>
using namespace std;

void swap(int &a,int &b)
{
    int temp;
    temp=a,a=b,b=temp;
    cout << "a=" << a << " b=" << b << endl;
}

int main()
{
    int i,j;
    cin >> i >> j;
    swap(i,j);
    cout << "i=" << i << " j=" << j << endl; 

    return 0;
}
运行结果

运行时输入2 5

a=5 b=2
i=5 j=2
  • 效率问题:在内置类型当中三种传递方式的效率上都差不多;在自定义类型当中,传引用的更高效一些,因为它没有对形参进行一次拷贝

3. 什么是内联函数?为什么要使用内联函数?

  • 定义: 当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。
  • 原因: 省去了调用的过程,加快程序运行速度(实现代码的重用,提高开发效率),但是可能使得程序的体积变大。

4. 什么是类的前向声明?使用类的前向声明时,要注意什么?

  • 类的前向声明: 是在引用未定义的类之前,声明该类,使编译器知道是一个类名。当程序中使用这个类名时,编译器就不会认为是错误,而类的完整定义可以在程序的其他地方。

  • 注意事项:

  1. 前向声明的类不能定义对象。
  2. 可以用于定义指向这个类型的指针和引用。
  3. 用于申明使用该类型作为形参或返回类型的函数。

类相互引用的循环依赖情况

class B;//类的前向声明
class A{//A类定义
    public:
        void f(B b);//以B类对象b为形参的成员函数
};
class B{//B类定义
    public:
        void f(A a);
};

5. 什么是先验条件(Precondition),什么是后置条件(Postcondition)?

  • 先验条件($Precondition$): 针对方法($method$),它规定了在调用该方法之前必须为真的条件
  • 后置条件($Postcondition$): 针对方法($method$),它规定了方法顺利执行完毕之后必须为真的条件

6. 什么是名称空间(namespace)?它的主要作用是什么?要使用一个名称空间中声明的标识符,方式有哪些?

  • 定义: 名称空间通过定义一种新的声明区域来创建命名的名称空间,一个名称空间中的实体不会和另外一个名称空间中的同名实体冲突.
  • 作用: 处理程序中常见的同名冲突
例子
#include <iostream>
using namespace std;

namespace mfc{
    int a;
    //...
}

namespace owl{
    int a;
    //...
}

int main()
{
    mfc::a=5;
    cout << "mfc::a= " << mfc::a << endl;
    owl::a=3;
    cout << "owl::a= " << owl::a << endl;
    return 0;
}
运行结果
mfc::a= 5
owl::a= 3
  • 方法
  1. 直接使用域解析符::.
    mfc::a=5;
    owl::a=3;
    
  2. 使用$using$ $declaration$(声明),一般用于单个元素类型.
using mfc::a;

a=5;
cout << "mfc::a= " << a << endl;
owl::a=3;
cout << "owl::a= " << owl::a << endl;
  1. 使用$using$ $directive$(指令),一般用于整体名字空间.
using namespace mfc;
a=5;
//...直接使用mfc中的成员(可不用域解析符)
owl::a=3;

7. 什么是重载(Overloading),解释并举例说明?能否根据返回值不同,对函数进行重载,为什么?

重载: 一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载($Overloading$)

void print(int i);
void print(string st);
void print(int i, int j);

原因: 不能根据返回值不同,对函数进行重载,容易产生歧义,当$main$函数调用Fun(1,2),编译器不知道选择哪个函数进行调用

int Fun(int a, int b);
long Fun(int a, int b);

Fun(1,2);

8. 如何防止一个头文件被多重包含?举例说明

使用条件编译

假设包含的文件名为x.h,写成如下:

#ifndef _x_h
#define _x_h
//...其他内容

#endif

9. dynamic-cast的作用是什么?试举例说明

dynamic-cast的作用
作用: 将基类的地址转换为派生类的地址,如果类型匹配则转换成功,否则转换失败,返回空地址。

转换原则:

  • 如果$p$是一个指向$B$类型的对象的指针,则转换成功;
  • 如果$p$不是一个指向$B$类型的对象的指针,则转换失败,返回一个空地址.
dynamic_cast<B *>(p);
例子
#include <iostream>
using namespace std;

class A {
public :
    virtual void say() {
        cout << "Class A" << endl;
    }
};

class B : public A {
public :
    virtual void say() {
        cout << "Class B" << endl;
    }
};


class C : public A {
public :
    virtual void say() {
        cout << "Class C" << endl;
    }
};

int main() {
    int op;
    cin >> op;
    A *p;
    switch (op % 2) {
        case 0: p = new B(); break;
        case 1: p = new C(); break;
    }
    //判断p到底是什么对象
    //将p转换为目标地址B *
    cout << dynamic_cast<B *>(p) << endl;
    //将p转换为目标地址C *
    cout << dynamic_cast<C *>(p) << endl;
    return 0;
}

文章作者: 保底不歪抽早柚
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 保底不歪抽早柚 !
评论
  目录