C++面经总结

C++面经总结(持续更新)

1、C++与 C 有什么区别?

  • 语言类型:C 是一种过程性编程语言,着重于以函数为基础的结构化编程;而 C++ 是一种多范式编程语言,支持面向对象编程(OOP)和泛型编程等多种编程范式。
  • 对象模型:C++ 支持类和对象的概念,可以通过封装数据和成员函数来实现面向对象编程。而在 C 中,没有直接支持类和对象的特性,只能通过结构体和函数来模拟。
  • 标准库:C++ 在标准库方面相比 C 更加丰富。除了包括 C 标准库外,C++ 还提供了标准模板库(Standard Template Library,STL),其中包含了许多容器、算法和迭代器等可复用的组件,能够极大地简化开发过程。
  • 面向对象特性:C++ 支持封装、继承和多态等面向对象编程的特性,使得代码可以更加模块化、可维护和可扩展。而在 C 中,没有原生的支持这些特性,需要手动进行实现。
  • 异常处理:C++ 提供了异常处理机制,允许程序在出现错误时抛出异常,并通过异常处理语句进行捕获和处理。而在 C 中,通常使用错误码等方式来处理错误。
  • 函数重载:C++ 支持函数重载,即可以定义多个同名函数,但参数类型、个数或顺序不同。这样可以根据具体的情况选择合适的函数进行调用。而在 C 中,函数重载是不允许的。
  • 名称空间:C++ 引入了命名空间(namespace)的概念,可以避免命名冲突,在不同的名称空间中定义相同名字的函数和变量。C 中没有命名空间的概念。

需要注意的是,C++ 是在 C 的基础上扩展而来的语言,因此 C 代码大多数也可以在 C++ 中正常编译和运行。同时,C++ 也提供了与 C 兼容的特性和语法,可以兼容部分 C 代码。

2、解释 C++ 的多态性(polymorphism)和继承性(inheritance)。

  • 继承性(Inheritance)是指一个类(称为派生类或子类)可以从另一个类(称为基类或父类)继承属性和方法。通过继承,派生类可以访问基类中的成员变量和成员函数,并且可以添加自己特定的成员变量和成员函数。继承关系形成了一种层次结构,使得代码可以更加模块化、可重用和易于扩展。
  • 多态性(Polymorphism)是指通过使用基类指针或引用,在运行时根据实际对象类型来调用对应的方法。具体而言,在继承关系中,当基类指针或引用指向派生类的对象时,可以在不知道对象的具体类型的情况下,调用基类中定义的虚函数(使用 virtual 关键字声明的成员函数)。此时,会根据对象的实际类型调用相应的派生类实现,实现动态绑定。这种能力使我们能够编写通用的代码,处理多个具体的子类对象,而无需针对每个子类都编写特定的代码。

通过多态性,我们可以实现以抽象的方式操作对象,增强了代码的灵活性和可扩展性。它允许我们以统一的方式处理不同的对象,并根据实际的对象类型调用正确的方法,而无需手动编写大量的条件判断语句。

需要注意的是,实现多态性的关键在于将基类中的成员函数声明为虚函数(使用 virtual 关键字)。派生类可以选择是否重写(override)基类的虚函数,以提供自己特定的实现。此外,C++ 还提供了纯虚函数(pure virtual function),用于定义接口,不能直接实例化的抽象类。派生类必须实现纯虚函数,以便被实例化。

3、解释虚函数(virtual function)和纯虚函数(pure virtual function)

  • 虚函数是在基类中使用 virtual 关键字声明的成员函数。通过声明为虚函数,基类可以为其派生类提供一个统一的接口,并允许在派生类中重写(override)该函数来提供自己的实现。当通过基类指针或引用调用虚函数时,程序会根据指针或引用指向的实际对象类型来确定应该调用的函数版本,即动态绑定。这使得我们能够以统一的方式操作不同的对象,而无需关心对象的具体类型。

  • 纯虚函数是在基类中使用纯虚函数语法定义的函数,没有实际的函数体,只提供了函数的签名。通过将一个或多个成员函数声明为纯虚函数,基类成为抽象类(Abstract Class),这意味着不能创建抽象类的实例,只能通过派生类来实现具体的功能。派生类必须实现纯虚函数才能被实例化。纯虚函数使得我们能够定义接口,强制要求派生类提供相应的实现。

在 C++ 中,可以通过纯虚函数使一个类成为抽象基类,提供接口规范而无需实际的实现细节。抽象基类可以作为通用框架使用,派生类通过实现纯虚函数来提供具体的功能。此外,派生类也可以选择重写基类的虚函数来改变或扩展基类的行为。

需要注意的是,包含纯虚函数的类是不能被直接实例化的,只能作为其他类的基类来派生子类。

4、什么是动态绑定(dynamic binding)?它的使用场景是什么?

动态绑定(Dynamic Binding),也称为运行时多态性,是面向对象编程中的一个重要概念。它指的是在程序运行时根据对象的实际类型确定所调用的函数版本。

使用动态绑定可以实现多态性,即通过基类的指针或引用调用虚函数时,根据指针或引用指向的实际对象类型来确定应该调用的函数版本。这样可以在不同的对象上调用相同的函数名,但执行的实际代码却可能不同,从而实现基于对象类型的行为差异。

动态绑定的使用场景主要包括以下几个方面:

  1. 多态性:通过在基类中声明虚函数并将其在派生类中重写,利用动态绑定可以在运行时根据对象的实际类型来调用适当的函数版本,实现多态的表现形式。这可以大大提高代码的可扩展性和灵活性。
  2. 抽象类和接口:通过将函数声明为纯虚函数,使得基类成为抽象类,无法直接实例化。派生类必须实现纯虚函数才能被实例化。动态绑定保证了当使用基类指针或引用调用纯虚函数时,实际调用的是对应派生类中实现的函数。
  3. 运行时类型识别(RTTI):通过使用动态绑定,可以在运行时判断对象的实际类型。在面向对象编程中,有时需要根据对象的实际类型进行特定的处理逻辑,而动态绑定使得这种运行时类型识别成为可能。

总之,动态绑定通过在程序运行时确定所调用的函数版本,实现了多态性和抽象类的特性。它广泛应用于面向对象编程中,提高代码的可扩展性、灵活性和可维护性。

5、解释 C++ 中的 const 关键字以及它的作用。

在C++中,const是一个关键字,用于声明常量(constant)。const关键字可以修饰变量、函数参数、成员函数以及成员函数的返回类型。

const关键字的作用有以下几个方面:

  • 声明常量:当const修饰变量时,表示该变量的值在初始化后不能被修改,它成为一个常量。这样可以提高程序的可读性和安全性,防止无意间修改了变量的值。

  • 只读访问:当const修饰函数参数时,表示该参数在函数内部不会被修改。这样可以确保函数内部不会意外地修改传入的参数,同时也能够通过适当的类型转换允许传入常量参数或表达式。

  • 防止函数修改对象状态:当const修饰成员函数时,表示该函数不会修改对象的状态(即类的数据成员),只能进行只读操作。这样可以给用户提供一种承诺,即调用该成员函数不会对对象做任何修改。

  • 返回常量对象:当const修饰成员函数的返回类型时,表示该函数返回的对象是常量对象,不能被修改。这样可以避免函数返回的对象被修改,增强代码的安全性。

  • 常量表达式:在C++11之后,可以使用const关键字定义常量表达式。常量表达式是一个在编译时就能确定值的表达式,可以用于数组大小、枚举值等需要常量表达式的地方。

6、什么是 RAII 技术?

RAII(Resource Acquisition Is Initialization)是一种C++编程技术,用于管理资源的获取和释放。它基于C++中对象的生命周期与作用域的特性,通过在对象的构造函数中获取资源,在析构函数中释放资源,从而确保资源的正确获取和释放。

RAII的核心思想是将资源的获取与对象的初始化绑定在一起,利用了对象在离开作用域时会自动调用析构函数的特性。这样可以避免由于忘记释放资源或发生异常而导致资源泄露的问题。

通过使用RAII,可以有效地管理各种类型的资源,包括内存、文件句柄、网络连接、互斥锁等。对于需要手动管理资源的情况,RAII可以简化代码,并提高程序的可靠性和安全性。

下面是一个示例代码,演示了如何使用RAII管理文件资源:

#include <iostream>
#include <fstream>

class File {
public:
    explicit File(const std::string& filename): file_(filename) {
        if (!file_) {
            throw std::runtime_error("Failed to open file.");
        }
    }

    ~File() {
        if (file_.is_open()) {
            file_.close();
        }
    }

    void Write(const std::string& data) {
        file_ << data;
    }

private:
    std::ofstream file_;
};

int main() {
    try {
        File myfile("example.txt");
        myfile.Write("Hello, RAII!");
    } catch (const std::exception& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

在上述代码中,File类封装了文件操作,构造函数打开文件资源,析构函数关闭文件资源。在main()函数中创建了一个File对象myfile,当myfile离开作用域时,其析构函数会被自动调用,从而释放文件资源。

RAII技术在C++中广泛应用,它是一种重要的资源管理方式,可以提高代码的可靠性、安全性和可维护性。

7、C++ 中有哪些内存管理方式?

在C++中,常见的内存管理方式包括以下几种:

  • 栈上分配:栈是用于存储局部变量和函数调用信息的一块内存区域。在函数调用时,局部变量会在栈上自动分配内存,并在函数返回时自动释放。这种分配方式速度快,但分配的内存大小是静态确定的,并且生命周期受函数作用域的限制。

  • 堆上分配:通过使用new和delete(或new[]和delete[])运算符,在堆(动态内存)上手动分配和释放内存。堆上分配的内存大小可以在运行时确定,并且对象的生命周期不受作用域的限制。需要手动管理内存的释放,避免内存泄漏。

  • 智能指针:智能指针是一种封装了指针的类模板,具备自动释放内存的能力。常见的智能指针包括std::shared_ptr、std::unique_ptr和std::weak_ptr。它们利用析构函数自动释放内存,避免了显式调用delete的问题,并且提供了方便的所有权管理和资源共享机制。智能指针可以帮助避免内存泄漏和悬挂指针等问题。

  • 容器:C++标准库提供了多种容器类,例如std::vector、std::list、std::map等。这些容器类在内部管理内存分配和释放的细节,通过自动调整内存大小来适应数据的增删操作。使用容器类可以简化内存管理,并提供更高级别的抽象和操作接口。

  • 自定义内存管理:在特定场景下,可以根据需要自定义内存管理方式。例如,重载类的operator new和operator delete来实现自定义的内存分配和释放策略。这种方式适用于一些特殊的性能优化需求,但要小心确保正确的内存管理和资源回收。

总结起来,C++中常见的内存管理方式包括栈上分配、堆上分配、智能指针、容器和自定义内存管理。不同的方式适用于不同的场景,选择合适的内存管理方式有助于提高程序的性能、可靠性和可维护性。使用智能指针和容器

8、解释智能指针(smart pointer)及其作用。

智能指针(smart pointer)是一种C++语言提供的特殊对象,用于管理动态分配的内存资源。它可以自动地在适当的时机释放所持有的内存,以避免内存泄漏和悬空指针等问题。

智能指针与常规指针相比具有以下几个优点和作用:

  • 自动内存管理:智能指针通过使用引用计数或所有权转移等机制,可以自动地跟踪所指向的内存资源的引用次数,并在引用计数为零时自动释放该资源。这样可以有效避免忘记释放内存、重复释放以及悬空指针等内存管理问题。

  • 安全可靠:智能指针可以提供更高级别的安全性和可靠性,实现了内存的自动回收,降低了出现内存泄漏和空悬指针的风险。

  • 标准化接口:C++标准库提供了多种类型的智能指针,如std::unique_ptr、std::shared_ptr和std::weak_ptr。它们都遵循统一的接口规范,使得在不同场景下使用智能指针变得更加便捷和统一。

  • 资源共享和转移:std::shared_ptr可以跟踪多个指针共享同一个资源,并在最后一个使用者释放资源时自动进行内存清理。std::unique_ptr支持所有权转移,允许将所管理的资源从一个智能指针转移到另一个智能指针,而不需要手动的内存释放和重新分配。

9、什么是拷贝构造函数(copy constructor)和赋值操作符(assignment operator)?

拷贝构造函数和赋值操作符是C++中的两个重要的特殊成员函数。

拷贝构造函数用于创建一个新对象,并将其初始化为另一个同类型对象的副本。它通常采用引用参数,以便可以访问要复制的对象,并且通常会使用深拷贝来确保新对象和原始对象是独立的。

赋值操作符用于将一个已经存在的对象的值赋给另一个同类型对象。它通常采用引用参数,并返回一个引用,以便可以使用链式赋值。它也应该使用深拷贝来确保新对象和原始对象是独立的。

class MyClass {
public:
    // 拷贝构造函数
    MyClass(const MyClass& other) {
        // 深拷贝
    }

    // 赋值操作符
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            // 深拷贝
        }
        return *this;
    }
};

深拷贝是在拷贝对象时,不仅复制对象本身,还要复制指向对象的所有指针所指向的内容。这意味着在新对象和原始对象之间有一个完全独立的拷贝,它们可以分别进行修改而不会互相影响。
相对的,浅拷贝只是简单地复制指向对象的指针,这意味着新对象和原始对象将共享同一块内存,因此改变其中一个对象的值也会影响另一个对象。
在使用动态内存分配时,深拷贝非常重要,因为如果不进行深拷贝,可能会导致内存泄漏或意外的行为。

10、解释引用(reference)和指针(pointer)之间的区别。

引用和指针都是C++中用于处理内存地址的概念,它们的主要区别在于以下几点:

  1. 操作符:引用使用&操作符进行声明和访问,而指针使用*操作符进行声明和访问。

  2. 初始化:引用必须在声明时进行初始化,并且不能更改其指向的对象,而指针可以在任何时候进行初始化和重新赋值。

  3. 空值:引用必须始终指向某个对象,而指针可以为空值(即指向空地址)。

  4. 类型:引用必须始终与其所引用的对象类型匹配,而指针可以通过类型转换来指向不同类型的对象。

  5. 内存管理:引用不需要显式的内存管理,而指针需要显式的内存分配和释放。

总的来说,引用通常用于函数参数和返回值以及对象别名,而指针则更适合于动态内存分配和数组操作。

11、C++ 中的 STL 是什么?它包含哪些常用的容器类和算法?

STL是C++标准库的一部分,是一个强大而灵活的工具集,提供了许多常用的容器类和算法,可以极大地简化C++程序的开发过程。STL是“标准模板库”的缩写,它主要由以下三个组件组成:

  1. 容器(Containers):提供了许多常用的数据结构,如向量(vector)、链表(list)、集合(set)、映射(map)等。

  2. 迭代器(Iterators):提供了一种通用的访问容器中元素的方法,使得算法可以独立于容器而工作。

  3. 算法(Algorithms):提供了许多常用的算法,如查找、排序、合并、拷贝等。

常用的容器类包括:

  • vector:动态数组
  • list:双向链表
  • set:集合
  • map:映射
  • queue:队列
  • stack:栈

常用的算法包括:

  • find:查找
  • sort:排序
  • reverse:翻转
  • copy:拷贝
  • merge:合并

使用STL可以大大提高C++程序的开发效率和代码质量。

六大部件:容器containers,迭代器iteratora,适配器adaptera,分配器allocators,算法algorithmas
,函数对象functors

12、解释迭代器(iterator)及其作用。

迭代器(iterator)是STL提供的一种通用的访问容器中元素的方法,它可以看作是一种类似于指针的对象,用于指向容器中的元素,使得算法可以独立于容器而工作。

迭代器的作用是将数据容器和算法分离开来,使得算法能够处理任何类型的数据容器,而不需要针对每一种数据容器都编写特定的算法。通过使用迭代器,我们可以遍历容器中的元素,读取或修改它们的值,甚至可以在容器中插入或删除元素。

迭代器有多种类型,包括正向迭代器、双向迭代器、随机访问迭代器等,不同类型的迭代器支持不同的操作。例如,随机访问迭代器支持随机访问操作,可以像指针一样进行加减运算;而正向迭代器只支持单向遍历操作,不能进行随机访问。

总之,迭代器是STL中非常重要的一部分,它使得STL具有了极高的灵活性和可扩展性,成为了C++程序开发中不可或缺的工具之一。

13、什么是函数对象(function object)?

函数对象(function object)是一种封装了函数或函数指针的对象,它可以像函数一样被调用,并且可以存储状态信息。函数对象可以看作是一种特殊的类对象,它通常重载了调用运算符(operator()),从而可以像函数一样被调用。

函数对象的主要作用是在STL算法中提供一种通用的函数调用方式,可以将算法和操作对象解耦,使得算法可以处理任何类型的操作对象,而不需要针对每一种操作对象都编写特定的算法。通过使用函数对象,我们可以将需要传递给算法的操作对象封装成一个可调用的函数对象,然后将其作为参数传递给算法。

STL中提供了许多标准的函数对象,如加法、减法、乘法、除法等,也可以自定义函数对象来满足特定的需求。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值