C++理论期末复习

下列关于异常处理的说法不正确的是( )


A.
异常处理的throw与catch通常不在同一个函数中,实现异常检测与异常处理的分离。


B.
catch语句块必须跟在try语句块的后面,一个try语句块后可以有多个catch语句块。


C.
在对函数进行异常规范声明时,若形参表后没有任何表示抛出异常类型的说明,它表示该函数不能抛出任何异常。


D.
catch语句块中,catch(…)表示该catch可以捕捉任意类型的异常,必须将catch(…)放在catch结构的最后。

C错,是的,使用catch(...)可以捕捉任何类型的异常,这表示函数可以抛出任何异常类型。

C++处理异常的机制是由检查、抛出和捕获3部分组成

问题
【多选】下列关于异常传播的说法正确的是()


A.
当在try块内部发生异常时,程序会立即退出当前的try块,并开始搜索匹配的catch块来处理该异常。


B.
如果在try块中没有找到匹配的catch块,异常会传播到调用栈的上一层,即调用该try块的函数或方法中


C.
过程会继续进行,直到找到匹配的catch块或者异常传播到程序的顶层(通常是main函数外部),此时程序会调用std::terminate并终止执行。

Standard Answer: A, B, C
  • 如果在 try 块中没有找到匹配的 catch 块,异常会传播到调用栈的上一层,即调用该 try 块的函数或方法中。这也是正确的。

问题
下列哪项最能描述C++中的异常?


A.
一种错误的检测机制


B.
一种错误的处理方法


C.
一种处理程序中意外情况的方法


D.
一种调试工具

Standard Answer: C

C.

一种处理程序中意外情况的方法

在以下代码中,哪一行是重新抛出异常的正确方法?
try {
throw std::runtime_error("error");
} catch (std::runtime_error& e) {
// 哪行是重新抛出异常?
}


A.
throw e;


B.
throw std::runtime_error(e);


C.
throw;


D.
rethrow;

Standard Answer: C

正确的答案是:

C. throw;

解释:

  • catch 块中,使用 throw; 可以重新抛出当前捕获的异常。这种方式可以确保抛出的异常保持其原有的类型和内容。

示例代码如下:

#include <iostream>
#include <stdexcept>

void functionThatThrows() {
    try {
        throw std::runtime_error("error");
    } catch (std::runtime_error& e) {
        std::cerr << "Caught an exception: " << e.what() << std::endl;
        throw; // 重新抛出异常
    }
}

int main() {
    try {
        functionThatThrows();
    } catch (const std::exception& e) {
        std::cerr << "Exception caught in main: " << e.what() << std::endl;
    }
    return 0;
}

在这个示例中,functionThatThrows 函数捕获到一个 std::runtime_error 异常,并使用 throw; 重新抛出该异常。在 main 函数中再次捕获并处理这个异常。

Question 16 - 单选题
问题
下列关于自定义异常的描述哪个是正确的?


A.
自定义异常必须继承自 std::exception


B.
自定义异常必须包含一个 what() 方法


C.
自定义异常不能包含成员变量


D.
自定义异常类可以不继承任何标准异常类

Standard Answer: D

不一定必须继承自std::expection

Question 18 - 单选题
问题
以下哪种异常不是 std::logic_error 的子类?


A.
std::invalid_argument


B.
std::domain_error


C.
std::out_of_range


D.
std::overflow_error

标准异常类是C++标准库提供的一组类,用于处理程序中的异常情况。这些异常类都继承自 std::exception 类。以下是一些常见的标准异常类:

  1. std::exception:所有标准异常类的基类。它提供了一个虚函数 what(),可以返回一个指示异常原因的C字符串。

  2. std::logic_error:继承自 std::exception,表示程序逻辑错误的异常。

    • std::invalid_argument:表示无效参数的异常。
    • std::domain_error:表示参数超出有效域的异常。
    • std::length_error:表示尝试创建的对象超出最大长度的异常。
    • std::out_of_range:表示访问超出有效范围的异常。
  3. std::runtime_error:继承自 std::exception,表示程序运行时错误的异常。

    • std::range_error:表示计算结果超出有效范围的异常。
    • std::overflow_error:表示算术运算上溢的异常。
    • std::underflow_error:表示算术运算下溢的异常。
  4. std::bad_alloc:继承自 std::exception,表示内存分配失败的异常(例如 new 操作符分配内存失败时会抛出此异常)。

  5. std::bad_cast:继承自 std::exception,表示类型转换失败的异常(例如 dynamic_cast 操作符失败时会抛出此异常)。

  6. std::bad_typeid:继承自 std::exception,表示访问 typeid 操作符的类型信息失败的异常。

  7. std::bad_function_call:继承自 std::exception,表示调用一个空的 std::function 对象时抛出的异常。

示例代码展示了如何使用这些标准异常类:

问题
以下代码段中,哪个 catch 块会捕获到 std::runtime_error?
try {
throw std::runtime_error("runtime error");
} catch (std::exception& e) {
// 代码块A
} catch (std::runtime_error& e) {
// 代码块B
} catch (...) {
// 代码块C
}


A.
代码块A


B.
代码块B


C.
代码块C


D.
不会被捕获

Standard Answer: A
  • std::runtime_errorstd::exception 的派生类,因此第一个 catch 块 (catch (std::exception& e)) 就会捕获到这个异常。
  • 后续的 catch (std::runtime_error& e)catch (...) 块将不会被执行,因为异常已经在第一个 catch 块中被处理了。

因此,标准答案是:

A. 代码块A

这意味着所有从 std::exception 派生的标准异常(包括 std::runtime_error)都会被代码块A所捕获。

关于stack,下列哪项描述是正确的?


A.
stack是一种先进先出(FIFO)的数据结构


B.
stack支持随机访问其中的元素


C.
stack提供的主要操作是push、pop和top等


D.
stack可以直接使用迭代器进行遍历

C

Question 14 - 单选题
下面代码段的输出结果是:

template <typename T>
T sum(T a, T b)
{
    cout<<"A"<<endl;
    return a+b;
}

template <>
double sum(double a, double b)
{
    cout<<"B"<<endl;
    return a+b;
}


int main()
{
    sum(1.1, 2.2);

    return 0;
}
​

A.
A


B.
B


C.
AB


D.
ERROR

Standard Answer: B

题目 14

知识点:模板特化 标准答案 B:模板特化允许为特定类型提供特殊的实现。本例中,针对 double 类型提供了特化版本,所以输出为 "B"。

Question 17 - 单选题
下述关于函数模板和类模板的说法中错误的是:


A.
调用函数模板时,编译器允许根据函数调用中所给出的实参类型来确定相应的模板实参


B.
调用函数模板时允许显示指定模板实参


C.
实例化类模板时,编译器允许不显式提供模板实参而根据构造函数的实参类型来推断模板实参


D.
实例化类模板时,必须显式地提供模板实参

Standard Answer: C
虚函数是如何实现动态绑定(或称为迟绑定)的


A.
通过在对象中添加指向虚函数表的指针


B.
通过在编译时确定函数地址


C.
仅在静态类型为基类时使用


D.
通过在每个类中复制函数代码

在面向对象编程中,动态绑定允许程序在运行时决定调用哪个函数版本。为了实现这一点,编译器为包含虚函数的类创建一个虚函数表(vtable),该表包含指向虚函数的指针。每个对象在其内部存储一个指向对应类的虚函数表的指针。

当一个虚函数被调用时,程序会通过对象中的指针找到对应的虚函数表,然后在该表中查找相应的函数指针,从而实现函数调用。这种机制确保了在运行时,根据对象的实际类型调用正确的函数版本,而不是编译时确定的版本。

因此,正确答案是A。

以下哪些情况会导致虚函数表的创建?


A.
定义至少一个虚函数的类


B.
使用虚继承的类


C.
包含纯虚函数的类(抽象类)


D.
所有类都会自动创建虚函数表

Standard Answer: A, C
函数重载与函数覆盖(Override)的主要区别是什么?


A.
重载仅限于非成员函数,覆盖则是成员函数特有的现象


B.
重载函数必须有相同的返回类型,覆盖则不必


C.
覆盖函数必须有相同的函数签名,重载则不必


D.
重载发生在不同文件中,覆盖必须在同一文件内进行

函数重载与函数覆盖(Override)的主要区别如下:

  1. 重载 (Overloading)

    • 重载是指在同一个作用域内,同名函数通过不同的参数列表(包括参数的数量、类型和顺序)来区分。
    • 函数重载可以发生在同一个类中或同一个命名空间中。
    • 重载函数不要求返回类型相同,编译器通过参数列表来区分不同的重载函数。
  2. 覆盖 (Overriding)

    • 覆盖是指在派生类中重新定义基类中的虚函数,覆盖函数必须具有与基类虚函数相同的函数签名(包括参数列表和返回类型)。
    • 覆盖只能发生在类的继承关系中,基类中的虚函数被派生类重新定义。
    • 覆盖函数必须有相同的返回类型和相同的参数列表,并且基类中的函数必须是虚函数。
从基类继承了哪些内容?


A.
构造函数及其析构函数


B.
Operator=() 成员


C.
友元


D.
以上所有均不是

Standard Answer: D

标准答案D是正确的:从基类继承时,以下内容不会被继承:

A. 构造函数及其析构函数:构造函数和析构函数不会被继承。派生类需要定义自己的构造函数和析构函数。不过,派生类可以调用基类的构造函数和析构函数。

B. Operator=() 成员:赋值运算符(operator=)不会被继承。派生类需要定义自己的赋值运算符。如果不定义,编译器会生成一个默认的赋值运算符。

C. 友元:基类的友元不会被派生类继承。友元关系是基类和友元之间的特定关系,派生类不会自动获得这种关系。

因此,正确答案是:

D. 以上所有均不是

总结:从基类继承时,不会继承构造函数及其析构函数、赋值运算符和友元关系。

c++的类型兼容规则所指的替代包括以下情况:


A.
父类指针可以直接指向子类对象


B.
子类对象可以直接赋值给父类对象


C.
子类对象可以直接初始化父类对象


D.
将父类对象直接赋值给子类对象

在C++中,类型兼容规则所指的替代主要包括以下情况:

A. 父类指针可以直接指向子类对象:这是正确的。C++支持向上转型(upcasting),即父类指针或引用可以指向子类对象。例如:

Base* basePtr = new Derived(); 

B. 子类对象可以直接赋值给父类对象:这是不正确的。子类对象不能直接赋值给父类对象,因为这会涉及到对象切割(object slicing),只保留基类部分的成员数据。正确的方式是使用指针或引用进行赋值。

C. 子类对象可以直接初始化父类对象:这是不正确的。子类对象不能直接初始化父类对象,同样会涉及对象切割问题。可以通过指针或引用的方式来关联。

D. 将父类对象直接赋值给子类对象:这是不正确的。父类对象不能直接赋值给子类对象,因为父类对象不具有子类对象的所有成员和行为。需要进行显式类型转换,并且需要确保这种转换是安全的。

综上所述,正确的替代情况是:

A. 父类指针可以直接指向子类对象

当重载函数被声明为友元函数时,以下哪个操作符可以被重载?


A.
[]


B.
()


C.
->


D.
|=

Standard Answer: D

标准答案D是正确的:当重载函数被声明为友元函数时,可以重载操作符|=。这是因为在C++中,友元函数可以重载所有的二元操作符,包括|=(按位或赋值操作符)。

下列哪个陈述是错误的?


A.
当运算符函数作为成员函数实现时,最左(或唯一)的操作数必须是该类的对象(或对象的引用)。


B.
只有当二元运算符的左操作数是该类的对象,或者一元运算符的单个操作数是该类的对象时,才会(由编译器隐式地)调用特定类的运算符成员函数。


C.
根据运算符是作为成员函数实现的还是作为非成员函数实现的,该运算符在表达式中的使用是不同的。


D.
选择非成员函数重载运算符的另一个原因是使该运算符可交换。

C. 根据运算符是作为成员函数实现的还是作为非成员函数实现的,该运算符在表达式中的使用是不同的

  • 这是错误的。运算符在表达式中的使用方式是相同的,无论它是作为成员函数还是非成员函数实现的。区别仅在于实现和调用方式。

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值