C++高频(三)

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 << "Dat

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值