C++高频八股

全局变量和局部变量和static的区别

全局变量和局部变量的主要区别在于它们的作用域和生命周期。

全局变量在程序的整个生命周期内都是有效的,如果没有被初始化,会被初始化为0或者空。

局部变量知识在定义他们的函数内部有效,生命周期是进入函数到离开函数的区间,在使用前必须初始化。

static变量的生命周期是整个程序执行期间,但其作用域仅限于定义它的函数。如果static变量没有被初始化,它们会被自动初始化为零。

简述C++的内存管理

堆是由程序在运行时动态申请的内存空间。在C++中,可以使用new关键字来申请堆内存,并使用delete关键字来释放已申请的堆内存。

栈内存用于存储局部变量和函数调用的信息。当函数被调用时,其参数和局部变量会被压入栈中。当函数执行完毕后,这些信息会被自动从栈中弹出,释放内存。

全局变量和静态变量被存储在全局/静态存储区。这部分内存在程序的生命周期内一直存在。

常量存储区:常量存储区用于存储常量,这部分内存也是在程序的生命周期内一直存在。

自由存储区:这是一块由malloc等函数分配的内存区域,结束使用后要用free等函数释放。

堆和栈的区别是什么?哪个更快一些?

内存管理:堆内存是动态分配的,可以在运行时决定分配多少内存。而栈内存是在编译时就已经确定的。

生命周期:堆内存的生命周期取决于程序员的管理,当使用new关键字分配了一块堆内存后,它会一直存在,直到使用delete将其释放。而栈内存的生命周期则取决于其作用域,当变量的作用域结束时,它占用的栈内存就会被自动释放。

性能:栈内存的分配和释放速度通常要比堆内存快。这是因为栈内存是以连续的、固定大小的块来管理的,所以分配和释放都非常快速。而堆内存则需要在运行时查找足够大的内存块来分配,所以速度相对较慢。

你理解的指针是什么东西?

指针存储的是内存地址,而不是具体的值。通过指针,我们可以访问和操作存储在特定内存位置的数据。

常量指针和指针常量区别?指针函数和函数指针区别?

常量指针是指向常量的指针:不能通过这个指针来改变它所指向的值,但你可以改变指针本身。例如,const int *ptr

指针常量是常量定义的指针,不能改变指针指向的地址,可以改变指针指向的值,例如,int *const ptr;。

函数指针是一个指向函数的指针变量,函数指针可以用来调用函数和传递函数作为参数。例如,void (*ptr)(int); 是一个函数指针,它指向一个接受一个整数参数并且没有返回值的函数。

指针函数是一个返回值为指针的函数。例如,int* func(); 是一个返回整数类型的指针的函数。

智能指针说一下

裸指针是一种直接指向内存地址的指针,需要手动管理内存的分配和释放,容易出现内存泄漏、野指针等问题。智能指针是 C++ 中的一种封装类,它们提供了自动化的内存管理机制,并通过析构函数来自动释放资源,可以避免内存泄漏和野指针的问题。

unique_ptr是一种独占所有权的智能指针,同一时间只能有一个unique_ptr指向给定的对象。当unique_ptr离开作用域或被删除时,它所指向的对象也会被删除。

shared_ptr是一种共享所有权的智能指针,每当有新的shared_ptr开始共享同一个对象时,引用计数器就会增加。每次调用shared_ptr的析构函数时,引用计数器都会减少。当引用计数器减少到0时(即没有任何std::shared_ptr再共享该对象),该对象就会被自动删除。

weak_ptr:这是一种弱引用的智能指针,它可以观察另一个智能指针(如shared_ptr)所拥有的对象,但不会增加该对象的引用计数。

什么是面向对象

在面向对象编程中,我们会把要解决的问题分解成各个对象,建立对象的目的是为了描叙某个对象在整个解决问题的步骤中的属性和行为

类与实例:类是对一类事物的抽象,实例则是类的具体表现。例如,我们可以将“人”定义为一个类,而具体的“张三”或“李四”则是“人”这个类的实例。

介绍面向对象的三大特性

面向对象编程的三大特性是:封装、继承和多态。

封装:封装是指将属性和方法打包在一起,形成一个“类”。

继承:继承是一种能够让某个类型的对象获得另一个类型的对象的属性和方法的机制。通过继承,我们可以创建一个父类,然后定义更具体的子类来继承父类的属性和方法。子类除了继承父类的特性外,还可以定义自己特有的特性。

多态:虚函数在基类中被声明,并可以在任何派生类中被重写。虚函数允许我们通过基类指针来调用派生类的这个函数。这种机制被称为多态。

虚函数是什么,和纯虚函数的区别

虚函数在基类中被声明,并可以在任何派生类中被重写。虚函数允许我们通过基类指针来调用派生类的这个函数。这种机制被称为多态。

而纯虚函数在基类中没有定义,要求任何派生类都要定义自己的实现方法。定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。纯虚函数的声明方式是在函数原型后加=0。

虚函数表是针对类还是针对对象的

虚函数表是针对类的,而不是对象。每一个类都有一张虚函数表,存储中这个类所有虚函数的入口地址。同一个类的两个对象共享类的虚函数表。当一个对象被创建时,它有一个指向虚函数表的指针。

如果派生类重写了基类的虚函数,那么派生类会在其自己的虚函数表中存储重写后的函数地址。如果派生类没有重写基类的虚函数,那么它会继承基类的虚函数地址,并将其复制到自己的虚函数表中。

拷贝构造函数和移动构造函数作用分别是什么

拷贝构造函数的作用是创建一个新的对象,并将原对象的值复制到新对象中。拷贝构造函数执行的是深拷贝操作,以确保新对象和原对象不共享同一块内存

移动构造函数的参数是一个右值引用表示将要被移动的对象。移动构造函数可以将一个对象的资源(如堆内存)“移动”到另一个对象,而不是创建一个完全新的副本。移动构造函数的工作原理是将声明的对象的指针指向新对象,并将临时对象的指针置空。

在这个例子中,类A有一个指针成员x。当我们创建一个新对象b并使用move(a)初始化它时,移动构造函数会被调用。在移动构造函数中,我们直接将a.x赋值给b.x,然后将a.x设置为nullptr。这样,我们就避免了深拷贝操作。

右值引用

移动构造函数接收一个右值引用参数,并将参数的资源移动到新创建的对象中。然后,它将参数置于有效但未指定的状态,即指向参数的指针被设置为nullptr。

move的作用

Move的主要作用是将一个左值强制转化为右值引用。移动语义允许我们直接转移对象的资源,而不是复制它们。

move函数常用于实现移动语义。当你有一个右值时,并且你想将其资源移动到另一个对象时,你可以使用move函数,避免了在构造新对象时时复制源对象内容。

STL使用过那些容器,说说各自查询时间复杂度 在C++的STL库中,常用的容器包括vector、deque、list、set、map、unordered_set、unordered_map等。这些容器的查询时间复杂度如下:

vector:采用一维数组实现,元素在内存连续存放。查看操作的时间复杂度为:O(1)

deque:采用双向队列实现,元素在内存连续存放。查看操作的时间复杂度为:O(1)。

list:采用双向链表实现,元素存放在堆中。查看操作的时间复杂度为:O(N)。

set/map/multiset/multimap:这四种容器采用红黑树实现,红黑树是平衡二叉树的一种。查看操作的时间复杂度近似为: O(logN)。

unordered_set/unordered_map/unordered_multiset/unordered_multimap:这四种容器采用哈希表实现。查看操作的时间复杂度为:O(1),最坏情况O(N)。

c++高频八股 文章被收录于专栏

c++高频八股

全部评论

相关推荐

10 73 评论
分享
牛客网
牛客企业服务