当前位置:   article > 正文

后端开发C++面试经验_c++后端面试

c++后端面试

本人23年十一月面试了上海诸多大厂C++后端开发岗位,以下是一些问的比较多的高频问题和答案,现在找暑期实习翻出来继续背诵。

堆与栈的主要区别

  • 内存管理:栈的内存管理是自动的,而堆是程序员手动分配和释放的内存区域new malloc// delete free。
  • 生命周期:栈上的对象随着函数调用的结束而销毁,而堆上的对象直到使用相应的释放命令才会被销毁。
  • 大小限制:栈空间通常比堆小,堆空间更大
  • 访问时间:栈上的数据访问通常比堆上的数据访问更快。
  • 内存分配方式:栈的内存分配和释放是连续的,而堆的内存分配和释放是随机的。
  • 存储顺序:栈遵循后进先出的原则,堆可以随机访问。
  • 风险:堆的不当使用可能导致内存泄漏,而栈则因为自动管理而不太容易出现此类问题。

什么样的数据结构可以实现栈

数组、vector和链表

C++中static和const起到的作用的区别是什么

  1. 在C++中,staticconst关键字都用于变量和函数的声明中,但它们的用途和含义有明显的区别:

    1. static关键字
      • static用于类成员变量时,它表示该变量属于类而非类的某个特定实例。这意味着该变量的一份拷贝被所有实例共享。无论创建了多少对象,静态成员变量只有一个存储空间
      • static用于类成员函数时,表明该函数可以在不创建类的实例的情况下调用,并且只能访问类的静态成员。
      • 在函数内部使用static声明局部变量时,该变量的生命周期延长到程序执行结束,而不是函数调用结束。
      • 在文件、函数或区块作用域中使用static时,它改变了变量的链接属性,使其成为内部链接。这意味着静态变量或静态函数仅在定义它的文件内可见,对其他文件是隐藏的。
    2. const关键字
      • const用于变量时,表明这个变量的值不可以被改变。尝试修改一个const变量的值会导致编译错误。
      • const用在类成员函数后面时,表明这个成员函数不会修改类的任何成员变量
      • const也可以用于指针,既可以声明指向常量的指针(指针指向的值不可改变),也可以声明常量指针(指针自身的值不可改变,即不能指向另一个地址)。

    示例

    cppCopy codeclass MyClass {
    public:
        static int staticVar;  // 静态成员变量
        int instanceVar;       // 实例成员变量
    
        static void staticFunc() {}  // 静态成员函数
    
        void constFunc() const {}    // 常量成员函数
    };
    
    int MyClass::staticVar = 0;
    
    void someFunction() {
        static int staticVar = 0;  // 静态局部变量
        const int constVar = 42;   // 常量局部变量
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这个示例中,staticVar是一个静态成员变量,所有MyClass的实例都共享这个变量。staticFunc是一个静态成员函数,它可以在没有MyClass实例的情况下被调用。constFunc是一个常量成员函数,它不能修改类中的任何成员变量。在someFunction函数中,staticVar是一个静态局部变量,它的值在函数调用之间保持不变,而constVar是一个常量局部变量,它的值不能被修改。

static在C语言中的应用,起到了什么作用

在C语言中,static关键字有两个主要用途,涉及变量的存储持续性和可见性。这些用途直接影响变量的生命周期和在程序不同部分的访问性。

  1. 存储持续性
  • 静态局部变量:这意味着它在程序的整个运行期间都存在,即使其定义的函数返回了。它在第一次函数调用时被初始化,并在后续的函数调用中保持其值。例如:
void function() {
    static int counter = 0;
    counter++;
    printf("Counter is %d\n", counter);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 每次调用function时,counter变量都会保持其值。
  1. 可见性
  • 静态全局变量:但如果一个全局变量被声明为static,它的可见性就限制在了它被声明的文件内。这意味着,尽管这个静态全局变量在程序的整个运行期间都存在,但它不能被其他文件中的函数直接访问。

    这有助于限制变量的作用域,提高代码的封装性。例如:

    static int globalVar = 0;
    
    • 1
    • globalVar只能在定义它的源文件内部被访问。
    1. 静态函数
    • 函数可见性:与静态全局变量类似,如果一个函数被声明为static,它的可见性就局限于它被定义的源文件。这意味着这个函数不能被其他源文件中的代码调用。这也是一种封装和隐藏实现细节的手段。

总结

在C语言中,static关键字主要用于管理变量和函数的存储持续性和可见性。通过static声明的局部变量具有静态存储持续性,而通过static声明的全局变量和函数具有文件级作用域,这有助于封装代码,限制对变量和函数的直接访问,从而提高程序的模块化和可维护性。

C++和C中的static区别

C++保留了C中static的所有用法,并在此基础上扩展了更多功能:

  1. 静态成员变量:所有对象共享同一个静态成员变量。88
  2. 静态成员函数:类中的静态成员函数可以在没有类实例的情况下被调用。它只能访问静态成员变量和其他静态成员函数。

**对象被new出来以后函数初始化过程中完成了哪些工作 **

当在C++的主函数(main)中通过new运算符创建对象时,对象的初始化过程遵循一定的步骤。这些步骤确保对象得到正确的内存分配,并且其成员得到适当的初始化。这个过程具体包括:

1. 内存分配

  • 动态内存分配new运算符首先在堆(heap)上为对象分配足够的内存。堆内存是由程序员管理的,与自动(栈)内存相比,其生命周期不由作用域决定。

2. 构造函数调用

  • 构造函数执行:分配内存后,对象的构造函数被调用。这一步初始化对象的状态。
  • 成员初始化列表:如果构造函数有初始化列表,它会按列表中的顺序(实际上是按成员在类中声明的顺序)初始化成员变量。
  • 构造函数体:完成成员初始化后,执行构造函数的主体部分。

3. 完成初始化–此时对象处于可用状态。

  • 对象状态:一旦构造函数执行完毕,对象被视为完全构造完成,
  • 返回指针new表达式返回指向新分配和初始化的对象的指针。

4. 对象使用

  • 通过指针操作:可以通过返回的指针来访问对象的公共成员和方法。
  • 生命周期管理:对象将继续存在,直到使用delete显式释放其内存。

深浅拷贝的区别

介绍一下深拷贝与浅拷贝

浅拷贝:原始对象和副本都指向相同的内存地址

深拷贝:拥有各自独立的内存地址。

data = new int(*(source.data)); 这行代码的含义是:“为复制后的对象创建一个新的整数,让这个新整数的值等于原始对象的整数值,并将这个新整数的内存地址赋值给复制后对象的 data 成员”。这正是深拷贝的核心,即创建一个值相同但独立的数据副本。

  1. 如果想让你用深拷贝拷贝一个字典,你怎么做

    要对这些容器进行深拷贝,可以定义一个含有这些容器成员的类,并提供一个自定义的复制构造函数来实现深拷贝。

    // 深拷贝构造函数
        DeepMapCopy(const DeepMapCopy& source) {
            data = new std::map<std::string, int>(*(source.data));
        }
    //定义了深拷贝还不够,还要定义行为
        // 插入键值对
        void insert(const std::string& key, int value) {
            (*data)[key] = value;
        }
    
        // 获取字典数据
        std::map<std::string, int> getMap() const {
            return *data;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

linux中线程和进程的区别

  • 资源分配和独立性

    进程是系统资源分配的独立单位,拥有独立的内存空间;

    线程cpu分配的,是轻量级的执行单元,共享其父进程的资源。

  • 创建和管理开销:线程的创建和管理比进程更有效率,因为线程共享大部分资源。线程创建开销小,进程开销大。

  • 通信和同步:线程间通信比进程间通信更简单和快速,但需要处理线程同步的问题。

问:C++中智能指针的工作原理是什么?

当然,智能指针是C++中管理动态内存的一种机制,它们封装了原始指针,在适当的时候自动释放所管理的内存,从而减少内存泄漏和野指针的问题。C++标准库提供了几种类型的智能指针,主要包括std::unique_ptrstd::shared_ptrstd::weak_ptr

  1. std::unique_ptr

std::unique_ptr保证同一时间只有一个unique_ptr可以指向一个对象。当unique_ptr被销毁时(例如,离开作用域时),它指向的对象也会被自动销毁。

  • 特点:所有权不可复制,但可以转移(通过std::move)。
  • 用途:当你需要确保一个对象有且只有一个所有者时,使用unique_ptr
cppCopy codestd::unique_ptr<int> ptr(new int(42));
// std::unique_ptr<int> ptr2 = ptr; // 错误:不能复制unique_ptr
std::unique_ptr<int> ptr2 = std::move(ptr); // 正确:转移所有权
  • 1
  • 2
  • 3
  1. std::shared_ptr

std::shared_ptr提供了共享所有权的语义。多个shared_ptr实例可以指向相同的对象,内部使用引用计数来跟踪有多少个shared_ptr指向同一个对象。当最后一个这样的shared_ptr被销毁时,所指向的对象也会被自动销毁。

  • 特点:多个指针可以共享同一个对象的所有权。
  • 用途:当你需要在程序的不同部分共享对同一个对象的访问时,使用shared_ptr
cppCopy codestd::shared_ptr<int> sharedPtr1(new int(42));
std::shared_ptr<int> sharedPtr2 = sharedPtr1; // 正确:共享所有权
  • 1
  • 2
  1. std::weak_ptr

std::weak_ptr是一种不控制对象生命周期的智能指针。它设计用来解决shared_ptr相互引用时可能导致的循环引用问题。weak_ptr需要与shared_ptr一起工作,它通过shared_ptr来观察所指向的对象,但不影响该对象的引用计数。

  • 特点:不拥有对象,只是一个观察者。
  • 用途:用来解决shared_ptr的循环引用问题或者在某些场景下检查对象是否仍然存在。
cppCopy codestd::shared_ptr<int> sharedPtr(new int(42));
std::weak_ptr<int> weakPtr = sharedPtr;
// std::shared_ptr<int> ptrFromWeak = weakPtr; // 错误:不能直接从weak_ptr创建shared_ptr
std::shared_ptr<int> ptrFromWeak = weakPtr.lock(); // 正确:创建一个新的shared_ptr
  • 1
  • 2
  • 3
  • 4

使用智能指针时,应该避免直接使用原始指针和手动管理内存,这样可以大大减少内存泄漏和其他内存管理错误。

问:C++11/14/17/20中有哪些新特性,你用过哪些?

C++11**:**

自动类型推断(auto)****: 允许编译器自动推断变量的类型,这使得代码更加简洁。

基于范围的for循环(range-based for loop)****: 用于更简洁地遍历容器。

Lambda表达式:** 提供了一种便捷的方式来定义匿名函数。

智能指针(如std::unique_ptr和std::shared_ptr): 管理动态内存的现代方式,帮助防止内存泄漏。

nullptr: 一个明确的空指针常量,取代了之前的NULL。

移动语义(move semantics)和右值引用**(rvalue reference)****

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/792561
推荐阅读
相关标签