• C++面试问题收集


    牛客网参考:【C++工程师面试宝典】学习说明_互联网校招面试真题面经汇总_牛客网 (nowcoder.com)

    Q:C++面向对象思想

    概述:面向对象技术中的对象就是现实世界中,某个具体的物理实体在计算机中的映射和体现,是模拟现实世界中的实体。我们可以通过设计类,然后再实例化产生一个对象。
    基本特征:抽象,封装,继承,多态
    View Code

    Q:多态以及实现

    就是向不同的对象发送同⼀个消息,不同对象在接收时会产⽣不同的⾏为(即⽅法)。即⼀个接⼝,可以实现多种⽅法。
    实现:多态其实⼀般就是指继承加虚函数实现的多态,对于重载来说,实际上基于的原理是,编译器为函数⽣成符号表时的不同规则,重载只是⼀种语⾔特性,与多态⽆关,与⾯向对象也⽆关,但这⼜是 C++中增加的新规则,所以也算属于 C++,所以如果⾮要说重载算是多态的⼀种,那就可以说: 多态可以分为静态多态和动态多态。
    静态多态其实就是重载,因为静态多态是指在编译时期就决定了调⽤哪个函数,根据参数列表来决定;
    动态多态是指通过⼦类重写⽗类的虚函数来实现的,因为是在运⾏期间决定调⽤的函数,所以称为动态多态,⼀般情况下我们不区分这两个时所说的多态就是指动态多态。动态多态的实现与虚函数表,虚函数指针相关。
    View Code

    Q:虚函数以及虚函数的实现原理

    C++中多态的表象,在基类的函数前加上 virtual 关键字,在派⽣类中重写该函数,运⾏时将会根据对象的实际类型来调⽤相应的函数。如果对象类型是派⽣类,就调⽤派⽣类的函数,如果是基类,就调⽤基类的函数。
    实际上,当⼀个类中包含虚函数时,编译器会为该类⽣成⼀个虚函数表,保存该类中虚函数的地址,同样,派⽣类继承基类,派⽣类中⾃然⼀定有虚函数,所以编译器也会为派⽣类⽣成⾃⼰的虚函数表。当我们定义⼀个派⽣类对象时,编译器检测该类型有虚函数,所以为这个派⽣类对象⽣成⼀个虚函数指针,指向该类型的虚函数表,这个虚函数指针的初始化是在构造函数中完成的。
    后续如果有⼀个基类类型的指针,指向派⽣类,那么当调⽤虚函数时,就会根据所指真正对象的虚函数表指针去寻找虚函数的地址,也就可以调⽤派⽣类的虚函数表中的虚函数以此实现多态。
    View Code

    Q:new/delete 和 malloc/free

    使用new操作符来分配对象内存时会经历三个步骤:
    
    第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
    
    第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
    
    第三部:对象构造完成后,返回一个指向该对象的指针。
    
    使用delete操作符来释放对象内存时会经历两个步骤:
    
    第一步:调用对象的析构函数。
    
    第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
    
    细节
    
    自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。
    
    C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。
    
    那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。
    
    new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
    
    在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new-handler。new_handler是一个指针类型,为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明于的一个标准库函数,set_new_handler的参数为new_handler指针,指向了operator new 无法分配足够内存时该调用的函数。
    
    使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
    
    区别
    1、new分配内存按照数据类型进行分配,malloc分配内存按照指定的大小分配;
    
    2、new返回的是指定对象的指针,而malloc返回的是void*,因此malloc的返回值一般都需要进行类型转化。
    
    3、new不仅分配一段内存,而且会调用构造函数,malloc不会。
    
    4、new分配的内存要用delete销毁,malloc要用free来销毁;delete销毁的时候会调用对象的析构函数,而free则不会。
    
    5、new是一个操作符可以重载,malloc是一个库函数。
    
    6、malloc分配的内存不够的时候,可以用realloc扩容。扩容的原理?new没用这样操作。
    
    malloc、calloc函数的实质体现在将一块可用的内存连接为一个链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块,然后将该内存块一分为二(一块与用户申请的大小一样,另一块就是剩下的字节)。接下来,将分配给用户的那块内存地址传给用户,调用free函数时,它将用户释放的内存块连接到空链上,最后空闲链表会被切成很多的小内存片段。
    
    realloc是从堆空间上分配内存,当扩大一块内存空间时,realloc试图直接从现存的数据后面的哪些字节中获得附加的字节,如果能够满足需求,自然天下太平,如果后面的字节不够,那么就使用堆上第一个足够满足要求的自由空间块,现存的数据然后就被拷贝到新的位置上,而老块则放回堆空间,这句话传递的一个很重要的信息就是数据可能被移动。
    
    clear allocation memory allocation real allocation
    
    7、new如果分配失败了会抛出bad_malloc的异常,而malloc失败了会返回NULL。
    
    8、申请数组时: new[]一次分配所有内存,多次调用构造函数,搭配使用delete[],delete[]多次调用析构函数,销毁数组中的每个对象。而malloc则只能sizeof(int) * n。
    View Code

    Q:C++的继承方式

    protected 成员和 private 成员类似,也不能通过对象访问。但是当存在继承关系时,protectedprivate 就不一样了:基类中的 protected 成员可以在派生类中使用,而基类中的 private 成员不能在派生类中使用。
    不同的继承方式会影响基类成员在派生类中的访问权限。
    
    1) public继承方式
    基类中所有 public 成员在派生类中为 public 属性;
    基类中所有 protected 成员在派生类中为 protected 属性;
    基类中所有 private 成员在派生类中不能使用。
    
    2) protected继承方式
    基类中的所有 public 成员在派生类中为 protected 属性;
    基类中的所有 protected 成员在派生类中为 protected 属性;
    基类中的所有 private 成员在派生类中不能使用。
    
    3) private继承方式
    基类中的所有 public 成员在派生类中均为 private 属性;
    基类中的所有 protected 成员在派生类中均为 private 属性;
    基类中的所有 private 成员在派生类中不能使用。
    View Code

    Q:析构函数是否定义为虚函数的区别

    1)析构函数定义为虚函数时:基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。
    
    (2)析构函数不定义为虚函数时:编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。
    View Code

    Q:智能指针shared_ptr的实现

        (构造函数)构造shared_ptr(公共成员函数)
        (析构函数)销毁shared_ptr(公共成员函数)
        operator=    shared_ptr赋值(公共成员函数)
        swap    交换内容(公共成员函数)
        reset    重置指针(公共成员函数)
        get    获取指针(公共成员函数)
        operator*    取消引用对象(公共成员函数)
        operator->    取消引用对象成员(公共成员函数)
        operator[]  (C++17)提供到被存储数组的带下标访问(公开成员函数)
        use_count    返回计数(公共成员函数)
        unique(C++20前)    检查是否唯一(公共成员函数)
        operator bool    检查是否不为空(公共成员函数)
        owner_before    基于所有者的共享指针排序(公共成员函数模板)
    
    非成员函数
        swap    交换shared_ptr对象的内容(函数模板)
        relational operators    关系运算符 ==, !=, <, <=, >, >= (函数模板 )
        ostream operator<<    将存储的指针的值输出到输出流(函数模板)
    
    具体功能:
        make_shared,make_shared_for_overwrite(C++20)    创建管理一个新对象的共享指针(函数模板)
        allocate_shared,allocate_shared_for_overwrite(C++20)    创建管理一个用分配器分配的新对象的共享指针(函数模板)
        static_pointer_cast,dynamic_pointer_cast,const_pointer_cast,reinterpret_pointer_cast    (C++17)应用 static_cast、dynamic_cast、const_cast 或 reinterpret_cast 到被存储指针(函数模板)
        get_deleter    返回指定类型中的删除器,若其拥有(函数模板)
    View Code
      1 #include <utility>
      2 #include <cstddef>
      3 
      4 class ref_count
      5 {
      6 public:
      7     int use_count() const noexcept { return count_; }
      8     void inc_ref() noexcept { ++count_; }
      9     int dec_ref() noexcept { return --count_; }
     10 
     11 private:
     12     int count_{1};
     13 };
     14 
     15 template <typename T>
     16 class Shared_ptr
     17 {
     18 public:
     19     constexpr Shared_ptr() noexcept = default;
     20     constexpr Shared_ptr(nullptr_t) noexcept : Shared_ptr() {}
     21     explicit Shared_ptr(T *ptr) : ptr_{ptr}
     22     {
     23         if (ptr_ != nullptr)
     24         {
     25             rep_ = new ref_count{};
     26         }
     27     }
     28     Shared_ptr(const Shared_ptr &rhs) noexcept : ptr_{rhs.ptr_}, rep_{rhs.rep_}
     29     {
     30         if (ptr_ != nullptr)
     31         {
     32             rep_->inc_ref();
     33         }
     34     }
     35     Shared_ptr(Shared_ptr &&rhs) noexcept : ptr_{rhs.ptr_}, rep_{rhs.rep_}
     36     {
     37         rhs.ptr_ = nullptr;
     38         rhs.rep_ = nullptr;
     39     }
     40     ~Shared_ptr() noexcept
     41     {
     42         if (rep_ != nullptr && rep_->dec_ref() == 0)
     43         {
     44             delete ptr_;
     45             delete rep_;
     46         }
     47     }
     48 
     49     Shared_ptr &operator=(const Shared_ptr &rhs)
     50     {
     51         Shared_ptr{rhs}.swap(*this);
     52         return *this;
     53     }
     54     Shared_ptr &operator=(Shared_ptr &&rhs)
     55     {
     56         Shared_ptr{std::move(rhs)}.swap(*this);
     57         return *this;
     58     }
     59     void reset() noexcept
     60     {
     61         Shared_ptr{}.swap(*this);
     62     }
     63     void reset(nullptr_t) noexcept
     64     {
     65         reset();
     66     }
     67     void reset(T *ptr)
     68     {
     69         Shared_ptr{ptr}.swap(*this);
     70     }
     71 
     72     void swap(Shared_ptr &rhs) noexcept
     73     {
     74         std::swap(ptr_, rhs.ptr_);
     75         std::swap(rep_, rhs.rep_);
     76     }
     77     T *get() const noexcept
     78     {
     79         return ptr_;
     80     }
     81 
     82     long use_count() const noexcept
     83     {
     84         return rep_ == nullptr ? 0 : rep_->use_count();
     85     }
     86     bool unique() const noexcept
     87     {
     88         return rep_->use_count() == 1;
     89     }
     90 
     91     T &operator*() const noexcept
     92     {
     93         return *ptr_;
     94     }
     95     T &operator->() const noexcept
     96     {
     97         return ptr_;
     98     }
     99 
    100     explicit operator bool() const noexcept
    101     {
    102         return static_cast<bool>(ptr_);
    103     }
    104 
    105 private:
    106     T *ptr_{nullptr};
    107     ref_count *rep_{nullptr};
    108 };
    109 
    110 template <typename T, typename... Args>
    111 auto make_Shared(Args &&...args)
    112 {
    113     return Shared_ptr<T>{new T(std::forward(args)...)};
    114 }
    View Code
  • 相关阅读:
    Apache、nginx 、lighttpd性能比较
    datapump
    ORA-0600
    在归档模式中,tablespace处于offline状态下,同样可以进行RMAN备份
    一个0级别增量备份小demo
    logminer
    statspack
    flashback table
    constraint
    linux进入单用户模式
  • 原文地址:https://www.cnblogs.com/ouyang_wsgwz/p/16374811.html
Copyright © 2020-2023  润新知