C++面试高频(三)

1.静态绑定和动态绑定的介绍⭐

静态类型:对象在声明时使用的类型,在编译期就已经确定

动态类型:指针变量或引用变量所指向对象的类型,在运行期才能确定

静态绑定:绑定的是静态类型,对象的函数和属性依赖于绑定的静态类型,发生在编译期

动态绑定:绑定的是动态类型,对象的函数和属性依赖于绑定的动态类型,发生在运行期

而非虚函数一般都是静态绑定,虚函数则是动态绑定。

以下是一个简单的示例代码:

#include<iostream>

class Base {
public:
    virtual void display() {
        std::cout << "Base class display function" << std::endl;
    }
};

class Derived : public Base {
public:
    void display() override {
        std::cout << "Derived class display function" << std::endl;
    }
};

int main() {
    Base baseObj;
    Derived derivedObj;

    Base* ptr = nullptr;

    ptr = &baseObj;
    ptr->display();  // 静态绑定,输出 "Base class display function"

    ptr = &derivedObj;
    ptr->display();  // 动态绑定,输出 "Derived class display function"

    return 0;
}


代码附录解释:

定义了一个基类Base和派生类Derived。这两个类都有一个名为display的函数,其中派生类重写了基类的display函数。在main函数中,我们创建了一个基类指针ptr,并分别将其指向基类对象和派生类对象。

在静态绑定的情况下,当我们通过指针ptr调用display函数时,由于ptr的静态类型是基类指针,所以编译器会根据基类的函数定义来解析它,并调用基类的display函数。因此,输出结果为"Base class display function"。

然而,在动态绑定的情况下,当我们通过指针ptr调用display函数时,由于ptr的动态类型是派生类对象,所以在运行时会根据对象的实际类型来解析函数调用,并调用派生类的display函数。因此,输出结果为"Derived class display function"。

这个示例展示了动态绑定对于虚函数的重要性。通过使用虚函数,我们可以实现在运行时根据对象的动态类型来调用适当的函数,而不仅仅局限于对象的静态类型。这种动态绑定的特性可以增加程序的灵活性和可扩展性。

总结一下静态绑定和动态绑定的区别:

静态绑定发生在编译期,动态绑定发生在运行期

对象的动态类型可以更改,但是静态类型无法更改

要想实现动态,必须使用动态绑定

在继承体系中只有虚函数使用的是动态绑定,其他的全部是静态绑定;

2.析构函数可以抛出异常吗?为什么不能抛出异常?⭐

异常点之后的代码不会执行:当析构函数抛出异常时,异常将导致程序流程跳转到异常处理代码,导致异常点之后的代码不会被执行。这可能会导致对象销毁过程中的必要动作无法执行,例如释放资源,从而引发资源泄漏等问题。

还有以下几点原因:

  1. 安全性:抛出异常可能导致资源泄漏或不一致的状态。
  2. 可追踪性:异常的发生会增加代码的复杂性和调试的困难。
  3. 可移植性:不同编译器可能对析构函数中的异常支持不同。

为了解决这些问题,一种常见的做法是在析构函数中尽量避免抛出异常,而是使用try-catch块捕获和处理可能发生的异常。通过在try块中执行资源清理操作,并在catch块中进行适当的异常处理,可以确保对象的销毁过程能够正常进行,同时提供更好的程序安全性和可追踪性。

如果析构函数抛出异常,并且在异常点之后的程序不会执行,造成了资源泄漏等问题,可以考虑以下解决方法:

  1. 使用智能指针:使用C++中的智能指针(如std::unique_ptr、std::shared_ptr)来管理资源,可以自动处理资源的释放,避免手动管理资源导致的错误和异常。智能指针的析构函数会自动释放资源,即使在析构函数中抛出异常,也可以保证资源的正常释放。
  2. 分离资源管理:将资源的释放操作从析构函数中分离出来,使用独立的函数或类来管理资源的释放。在析构函数中调用这些资源管理函数,如果资源释放过程中发生异常,可以通过合适的方式处理异常,避免资源泄漏。
  3. 做好异常处理:在析构函数中合理地使用异常处理机制,例如使用try-catch块捕获异常,并在catch块中适当地处理异常。这样可以保证即使在析构过程中发生异常,也不会导致程序崩溃或其他严重问题。

3.什么情况下会调用拷贝构造函数?⭐

拷贝构造函数是类中特殊的构造函数,用于创建一个新的对象并将其初始化为同一类的另一个对象的副本。它通常用于在以下情况下进行对象的复制:

1.对象通过值传递或返回时:当对象作为函数参数按值传递或作为函数返回类型时,会触发拷贝构造函数的调用。这是因为在这些情况下,需要创建一个新的对象副本来传递给函数或作为返回值。

代码示例:

#include <iostream>

class MyClass {
public:
  MyClass(int value) : data(value) {
    std::cout << "Constructor called: " << data << std::endl;
  }

  MyClass(const MyClass& other) : data(other.data) {
    std::cout << "Copy constructor called: " << data << std::endl;
  }

  int getData() const {
    return data;
  }

private:
  int data;
};

void doSomething(MyClass obj) {
  std::cout << "Data inside doSomething: " << obj.getData() << std::endl;
}

MyClass createObject(int value) {
  return MyClass(value);
}

int main() {
  MyClass obj1(10);
  doSomething(obj1);

  MyClass obj2 = createObject(20);
  std::cout << "Data inside main: " << obj2.getData() << std::endl;

  return 0;
}

输出:
Constructor called: 10
Copy constructor called: 10
Data inside doSomething: 10
Constructor called: 20
Copy constructor called: 20
Data inside main: 20


代码解释:代码中,MyClass类的拷贝构造函数被调用来创建函数doSomething的参数对象和createObject的返回值对象。简化后的代码保留了拷贝构造函数的调用,并且输出了复制过程中的信息。

2.使用一个对象初始化另一个对象:当使用一个对象来初始化另一个对象时,会调用拷贝构造函数。这包括在创建一个新对象并用已存在的对象初始化它时,或者通过赋值运算符进行对象的初始化。

#include <iostream>

class MyClass {
public:
  MyClass(int value) : data(value) {
    std::cout << "Constructor called: " << data << std::endl;
  }

  MyClass(const MyClass& other) : data(other.data) {
    std::cout << "Copy constructor called: " << data << std::endl;
  }

  int getData() const {
    return data;
  }

private:
  int data;
};

int main() {
  MyClass obj1(10);  // 调用构造函数进行初始化
  MyClass obj2(obj1);  // 使用obj1对象初始化obj2对象

  std::cout << "Data in obj2: " << obj2.getData() << std::endl;

  MyClass obj3 = obj1;  // 使用赋值运算符进行对象的初始化

  std::cout << "Data in obj3: " << obj3.getData() << std::endl;

  return 0;
}
输出:

Constructor called: 10
Copy constructor called: 10
Data in obj2: 10
Copy constructor called: 10
Data in obj3: 10

代码解释:代码中,MyClass类的拷贝构造函数被调用来初始化obj2obj3对象。obj2通过使用已存在的obj1对象进行初始化,而obj3通过使用赋值运算符进行初始化。

4.mutable和volatile关键字功能对比?

mutable关键字

  • mutable关键字用于修饰类的成员变量,在常量成员函数中允许被修改。
  • 默认情况下,常量成员函数不允许修改类的成员变量,而使用mutable关键字可以解除这个限制,允许在常量成员函数中修改被mutable修饰的成员变量。
  • mutable关键字适用于一些内部状态需要在常量成员函数中更新的情况,例如缓存结果或需要记录操作次数等。

volatile关键字

  • volatile关键字用于修饰变量,在多线程、硬件IO和中断处理等场景中,告诉编译器该变量的值是可能发生变化的,需要特殊对待。
  • 为了告诉编译器不要对该变量进行优化,以防止对变量读取和写入的优化可能导致错误的行为。
  • volatile适用于需要和外部环境进行交互的变量,比如硬件寄存器的状态、多线

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

c++/嵌入式面经专栏 文章被收录于专栏

BG双9,目前在某外企。打算把之前校招时做的笔记通过专栏发出来,本专栏适合于C/C++、嵌入式方向就业的同学,本篇面经总结数千篇面经的知识集合,实时更新全网最新的嵌入式/C++最新内容,囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构、数据库等一系列知识点,在我看来这些是求职者在面试中必须掌握的知识点。最后呢祝各位能找到自己合适的工作。。

全部评论
创建对象数组用的不是构造函数吗?,为什么是拷贝构造函数
1 回复 分享
发布于 2023-10-31 09:58 重庆
7.3普通成员函数被存储在类的对象中,这句话是错的吧
点赞 回复 分享
发布于 05-10 15:36 山东
C++中,全局变量和静态变量的初始化行为有所不同。全局变量和静态变量在C++中的初始化推迟至它们"首次用到"时才进行,这是C++标准规定的行为。是这样子吗?感觉怪怪的。
点赞 回复 分享
发布于 2023-11-29 19:06 广东

相关推荐

咦哟,从去年八月份开始长跑,两处实习转正都失败了,风雨飘摇,终于拿到offer了更新一下面试记录:秋招:多部门反复面试然后挂掉然后复活,具体问了啥已经忘了,只是被反复煎炸,直至焦香😋春招:base北京抖音hr打来电话说再次复活,准备面试,gogogo北京抖音一面:六道笔试题:1.promise顺序2.定义域问题3.flat展开4.并发请求5.岛屿数量算法(力扣)深度,广度都写6.忘记了,好像也是算法,难度中等其他问题多是框架底层设计,实习项目重难点~~~秒过😇北京抖音二面:三道笔试题:(为什么只有三道是因为第三道没做出来,卡住了)1.中等难度算法(忘记啥题了,应该是个数组的)2.认识js的继承本质(手写继承模式,深入js的面相对象开发)3.手写vue的响应式(卡在了watch,导致挂掉)---后知后觉是我的注册副作用函数写得有问题,有点紧张了其他题目多是项目拷打,项目亮点,对实习项目的贡献~~~第二天,挂,but立马复活转战深圳客服当天约面深圳客服一面:六道笔试题,由于面过太多次字节,面试官叫我直接写,不用讲,快些写完😋,具体都是些继承,深拷贝(注意对数组对象分开处理,深层次对象,循环引用),加中等难度算法题~~~秒过深圳客服二面:口诉八股大战:大概囊括网络,浏览器渲染原理,动画优化,时间循环,任务队列等等(你能想到的简单八股通通拉出来鞭尸😋)算法题:笔试题6道:1:找出数组内重复的数,arr[0]-arr[n]内的数大小为[1-n],例如[1,2,2,3,3]返回[2,3],要求o(n),且不使用任何额外空间(做到了o(n),空间方面欠佳,给面试官说进入下一题,做不来了)2:原滋原味的继承(所以继承真滴很重要)3:力扣股票购买时机难度中等其他滴也忘记了,因为拿到offer后鼠鼠一下子就落地了,脑子自动过滤掉可能会攻击鼠鼠的记忆😷~~~秒过深圳客服三面:项目大战参与战斗的人员有:成员1:表单封装及其底层原理,使用成本的优化,声明式表单成员2:公司内部库生命周期管理成员3:第三方库和内部库冲突如何源码断点调试并打补丁解决成员4:埋点的艺术成员5:线上项目捷报频传如何查出内鬼成员6:大文件分片的风流趣事成员7:设计模式对对碰成员8:我构建hooks应对经理的新增的小需求的故事可能项目回答的比较流利,笔试题3道,都很简单,相信大家应该都可以手拿把掐😇~~~过过过无hr面后续煎熬等待几天直接hr打电话发offer了,希望大家也可以拿到自己心仪的offer
法力无边年:牛哇,你真是准备得充分,我对你没有嫉妒,都是实打实付出
查看19道真题和解析
点赞 评论 收藏
分享
在看数据的傻狍子很忙碌:学生思维好重,而心很急,自己想想真的能直接做有难度的东西吗?任何错误都是需要人担责的,你实习生可以跑路,你的同事领导呢
点赞 评论 收藏
分享
评论
3
26
分享

创作者周榜

更多
牛客网
牛客企业服务