C++_问答汇总_2023_自用

C++是一种通用编程语言,具有高级抽象、强类型和编译性能等特点。C++语言具有许多特性,包括面向对象编程、模板、多态、运算符重载等等。它广泛应用于各种领域,如系统软件、嵌入式系统、游戏开发、科学计算等等。

在这里插入图片描述

1、C++11相对于C++98的新特性?

类型推导:auto、decltype
右值引用
nullptr关键字:可以明确指定空指针,避免和整型值0混淆
范围for循环:可以遍历容器、数组和其它可迭代对象
列表初始化
lambda表达式:可以定义匿名函数,使代码更加灵活
静态断言:在编译时检查程序的正确性
强类型枚举:可以避免枚举类型的值被隐式地转换为整型

2、int和float相互转换是否有精度损失?

Int转float会有精度损失,这与float类型在内存中的存储方式有关(float和double不一样),浮点数转换为整型数会有小数截断问题

3、函数里面是否可以嵌套函数?

可以在函数中嵌套函数的,被嵌套的函数称为内部函数或局部函数。内部函数只在包含它的函数的作用域内可见,对于函数外部的其他部分来说是不可见的。内部函数可以访问包含它们的函数的局部变量和参数,但不能访问外部函数的局部变量和参数。

在C++11之前,内部函数必须在外部函数的开始处先进行声明,然后再进行定义。而在C++11之后,可以在函数中直接定义内部函数,而无需提前声明。

4、结构体里是否可以定义结构体?

可以

结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针。

结构体的相互引用(mutual reference),就是说在多个结构体中,都包含指向其他结构体的指针。

5、仿函数functor?

行为类似函数,可作为算法的某种策略,又称为函数对象(Function Object)是一个能行使函数功能的类,即使一个类的使用看上去像一个函数,从实现角度来看,仿函数是一种重载了operator()的class或者class template。

6、lamda表达式

Lambda表达式是C++11中引入的一种新特性,它是一种用来创建匿名函数的语法

Lambda表达式在C++中广泛应用,可以用于STL算法、函数对象等等方面。它可以提高代码的简洁性和可读性,同时也提供了一种方便的方式来定义临时的函数对象。

7、Unsigned int和unsigned long 在32位和64位下分别是多大?

在这里插入图片描述

8、this指针?

this指针是一个指向当前对象的指针。当我们在一个成员函数中调用成员变量或成员函数时,编译器会自动将this指针作为一个隐藏参数传递给该函数,以便在函数内部能够访问当前对象的成员。

this指针可以用来解决成员变量和函数参数同名的问题。在函数内部,如果有一个局部变量和一个成员变量同名,可以使用this指针来访问成员变量,如:

class MyClass {
public:
    void setX(int x) { this->x = x; }
private:
    int x;
};

this指针存在的目的:
保证每个对象拥有自己的数据成员,但共享处理这些数据成员的代码

9、重载和重写?

overload重载:是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。

override重写(覆写):是指派生类中存在重新定义的函数,以实现不同的功能,一般是用于子类在继承父类时,重写父类方法。其函数名、参数列表、返回值类型、所抛出的异常,所有都必须同基类中被重写的函数一致。被重写的方法不能为private。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。

  • 范围区别:重载是在同一个作用域内,定义多个同名函数,通过参数的不同来区分;而重写是在不同的类之间,子类重新定义父类的虚函数。
  • 参数区别:重载可以改变函数的参数类型、个数或顺序,但不能改变函数的返回值类型和函数名;而重写必须具有与被重写函数相同的函数名、参数列表和返回值类型。
  • virtual:重写的基类函数必须有virtual修饰;重载函数和被重载函数可以被virtual修饰,也可以没有。
  • 重载是静态绑定,即在编译期间就确定了调用的函数;而重写是动态绑定,即在运行期间根据实际对象的类型来确定调用的函数。

使用多态是为了避免在父类里大量重载引起代码臃肿且难于维护。

重写和重载的本质区别是:加入了override的修饰符方法,此方法始终只有一个被使用的方法。

10、static关键字的作用?
  • 修饰全局变量时:表明一个全局变量只对定义在同一文件中的函数可见(只能该文件内访问)
  • 修饰局部变量时:表明该变量的值不会因为函数终止而丢失
  • 修饰函数时:表明该函数只在同一文件中调用(只能该文件内访问)
  • 修饰类的数据成员:表明对该类所有对象数据成员只有一个实例,即该实例归所有对象共有
  • 修饰类的成员函数:该成员函数先于类实例存在而存在,且只能访问静态成员

作用:用来控制变量的存储方式和可见性,实现多个对象之间的数据共享+隐藏,并且使用静态成员还不会破坏隐藏规则;默认初始化为0;静态变量不会被重复初始化,不会在函数结束时销毁

11、vector扩容机制?

vector是C++ STL中的容器之一,是一个动态数组,可以自动扩容。当vector中的元素数量超过其容量时,vector会自动重新分配一块更大的内存,将原有元素拷贝到新的内存中,并释放原有内存。

vector的扩容机制主要涉及以下几个概念:

容量(capacity):vector的容量是指它当前分配的内存能够容纳的元素数量,而不是实际元素数量。当元素数量超过容量时,vector需要扩容。

大小(size):vector中实际存储的元素数量。

增量策略(growth policy):当vector需要扩容时,它会分配一块新的内存,并将原有元素拷贝到新的内存中。vector的增量策略决定了新的容量应该是多少,通常是原有容量的两倍。

指向数据的指针(pointer to data):vector中存储元素的内存块的首地址。

当vector需要扩容时,它会执行以下步骤:

计算新的容量,通常为原有容量的两倍。

分配一块新的内存,大小为新的容量。

将原有元素拷贝到新的内存中。

释放原有内存。

更新vector的容量和指向数据的指针。

vector的扩容机制可以提高效率,但同时也会带来额外的开销。因此,在实际使用中,应尽可能减少vector的扩容次数,可以通过预分配足够的容量来避免频繁扩容。

12、请说出 const char * p 、 char const *p 与 char * const p的区别?
  • 声明一个指向字符或字符串常量的指针,p所指向的内容不可更改(p是指向char类型的指针,const表示p指向的内容为const类型不可更改,不能指向const char类型)
  • 没有const*的运算,所以与前面的一样
  • 从右至左,Const修饰的是p,p中的值不可更改(p是一个地址)
13、(有哪些强制类型转换?)static_cast、dynamic_cast、reinterpret_cast 和 const_cast 几种 C++ 类型转换符的区别?

C++是一种强类型语言(更加安全)

  • const_cast用来将对象的常量属性移除,使常量可以被修改,针对常量指针、常量引用、常量对象。常量指针转换为非常量指针,并且仍然指向原来的对象。常量引用被转换为非常量引用,并且仍然指向原来的对象。
  • dynamic_cast用来处理一种“安全向下转换”,具有类型检查功能,比static_cast更安全,转换后必须是类的指针、引用或void*,基类要有虚函数,可以交叉转换。dynamic本身只能用于存在虚函数的父子关系的强制类型转换;对于指针,转换失败则返回nullptr,对于引用,转换失败会抛出异常
  • reinterpret_cast是用于底层的强制转换,导致不可移植的结果(eg:将一个指针转换成数组、将整形转换成指针)
  • static_cast用来处理隐式转换,可以将int转换为float,也可以将char转换为int,将指向基类的指针转换为指向子类(派生类)的指针,任何类型的表达式转化为void类型,没有运行时类型检查来保证转换的安全性
14、Volatile作用?

与const对立,是类型修饰符,影响编译器编译的结果,用该关键字声明的变量随时可能发生改变

当一个值可能随时会发生改变时,编译器就不能去假设这个变量,优化器每次用到这个变量都必须去内存重新读取,而不是使用保存在寄存器里边的值。

作用:确保本条指令不会因编译器的优化而省略,且要求每次直接读值,保证对特殊地址的稳定访问

15、new和malloc的区别?
  • new是操作符,而malloc是函数。
  • new在调用的时候先分配内存,在调用构造函数,释放的时候调用析构函数;而malloc没有构造函数和析构函数。
  • malloc需要给定申请内存的大小,返回的指针需要强转;new会调用构造函数,不用指定内存的大小,返回指针不用强转。
  • new从自由存储区上为对象动态分配内存空间,malloc从堆上动态分配内存
  • new/delete 可以被重载;malloc/free允许被重载
  • new发生错误时抛出bac_alloc异常,malloc分配内存失败时返回NULL
  • new可以用来创建对象和对象数组,创建对象不一定需要定义初始值
16、内联函数和普通函数的区别?

内联函数比普通函数多了关键字inline;以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。

如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗,内联函数避免了函数调用的开销;普通函数有调用的开销;
普通函数在被调用的时候,需要寻址(函数入口地址);内联函数不需要寻址。

内联函数有一定的限制,内联函数体要求代码简单,不能包含复杂的结构控制语句(内联函数内不允许用循环语句和开关语句。);普通函数没有这个要求。

17、C和C++的区别?

C语言是C++的子集,C++可以很好兼容C语言。但是C++又有很多新特性,如引用、智能指针、auto变量等;
C++是面对对象的编程语言;C语言是面对过程的编程语言;
C语言有一些不安全的语言特性,如指针使用的潜在危险、强制转换的不确定性、内存泄露等。而C++对此增加了不少新特性来改善安全性,如const常量、引用、cast转换、智能指针、try—catch等等;
C++可复用性高,C++引入了模板的概念,后面在此基础上,实现了方便开发的标准模板库STL。C++的STL库相对于C语言的函数库更灵活、更通用。

18、指针怎么分配内存?分配好后怎么判断是否分配成功?

在 C++ 中,可以使用 new 运算符来动态地分配内存。分配内存后,可以通过判断指针是否为空来判断分配内存是否成功。如果分配失败,则指针将为 nullptr。或者使用 try-catch 块来捕获分配内存时可能抛出的 std::bad_alloc 异常。在使用完动态分配的内存后,应当使用 delete 运算符释放内存,以避免内存泄漏。

19、指针和引用的区别?

引用总是指向一个对象;指针可能不指向一个对象

指针存放某个对象的地址,其本身就是变量,本身就有地址,可以有指向指针的指针;可变性:包括其所指向的地址的改变和其指向的地址中所存放数据的改变

引用就是变量的别名,不可改变,必须初始化

20、指针函数和函数指针的区别?

指针函数是一个函数,其返回的是一个指针。

函数指针则是一个指向函数的指针变量。它可以指向任何类型的函数,包括带参数和不带参数的函数,以及带返回值和不带返回值的函数。

21、内存对齐机制?

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

内存对齐(或内存对其)是计算机组成中的一个概念,它是指存储器中的数据在分配存储空间时,按照一定的规则排列,以使存取数据的时间效率最高。CPU从内存中取出数据的时候,通常会以缓存行(Cache Line)为单位进行,如果数据的地址没有对齐到缓存行的起始地址,就会跨两个缓存行访问,这种情况下会导致额外的开销。

内存对齐的规则如下:

数据类型对齐规则:不同的数据类型有不同的对齐方式,通常按照数据类型的大小进行对齐,例如 char 是 1 字节对齐,int 是 4 字节对齐,double 是 8 字节对齐。
结构体对齐规则:结构体的每个成员都要按照自身的对齐方式进行对齐,同时结构体本身也要进行对齐,对齐大小取结构体中最大数据成员的对齐值。
堆内存和栈内存对齐规则:堆内存和栈内存的对齐方式一般取决于操作系统和编译器的实现,一般情况下按照 8 字节对齐。

内存对齐的优势在于能够提高程序的运行效率和性能,但是它也会占用更多的存储空间,特别是对于结构体和类对象这样的复合类型。

22、宏定义和内联函数的区别?

define:
定义预编译时处理的宏,只是简单的字符串替换,无类型检查,不安全

inline:
是先将内联函数编译完成生成了函数体直接插入被调用的地方,减少了压栈、跳转和返回的操作,没有普通函数调用时的额外开销;内联函数是一种特殊的函数,会进行类型检查;对编译器的一种请求,编译器有可能拒绝这种请求

23、全局变量、局部变量、静态变量的联系和区别?
作用域:全局变量的作用域是整个程序,局部变量的作用域只在其定义的函数或语句块内部,而静态变量的作用域与其定义的位置有关。局部静态变量在函数内部定义,作用域是整个函数,而全局静态变量在文件内定义,作用域是整个文件。

存储位置:全局变量和静态变量都是存储在静态存储区中,而局部变量通常存储在栈中。

生命周期:全局变量的生命周期从程序开始到程序结束,而局部变量和静态变量的生命周期与其作用域有关。局部变量在其定义的函数或语句块执行完毕后被销毁,而静态变量在程序执行期间一直存在。

初始化:全局变量和静态变量在程序开始时自动初始化为0或空值,而局部变量不会自动初始化。未初始化的变量将包含随机值,因此必须手动初始化。
24、traits的原理及其作用?

Traits是一种编程技术,其原理是通过模板特化或模板偏特化来提供类型相关信息的统一访问接口。Traits的作用是将类型相关信息从算法或类中分离出来,使得代码更加通用、灵活和可维护。

具体来说,Traits的原理是通过定义一个模板类或模板函数,其中包含一个或多个静态成员函数,这些静态成员函数用于返回关于类型T的信息,例如类型T的大小、类型T是否是指针类型、类型T是否是类类型等。然后在算法或类中使用这些静态成员函数来获取类型相关信息,而不必直接操作类型本身。

Traits的作用是提高代码的可维护性和可复用性。通过使用Traits,可以将类型相关的信息从代码中抽象出来,使得代码更加通用、灵活和可扩展。例如,一个排序算法可以使用Traits来获取元素类型的大小,从而不必针对每种元素类型都写一份排序代码。又如,一个泛型容器可以使用Traits来判断元素类型是否支持某种操作,从而保证代码的类型安全性。

25、strlen和sizeof的区别?

strlen是函数,sizeof是运算符;sizeof()返回所占总空间的字节数;strlen()返回字符数组或字符串所占的字节数;

strlen只能计算以’\0’结尾的字符数组的长度,而且计算结果不包括’\0’;而sizeof可以计算任意类型或变量的大小,包括数组、结构体、指针等,计算结果包括所有元素的大小。

26、union和struct的区别?

主要区别在于内存布局和成员变量的访问方式

结构体是一种能够容纳多个不同类型的成员变量的数据结构,它的每个成员变量都可以独立地访问和修改。结构体的内存布局是将各个成员变量按顺序依次存储,每个成员变量都有自己的内存地址。

联合体中的成员变量共用同一块内存空间,它们的值会相互覆盖。联合体的内存布局是将各个成员变量放在同一块内存空间中,不同的成员变量使用同一块内存,可以互相覆盖。

27、class和struct的区别?

struct 一般用于描述一个数据结构集合,而 class 是对一个对象数据的封装;
struct 中默认的访问控制权限是 public 的,而 class 中默认的访问控制权限是 private 的;
在继承关系中,struct 默认是公有继承,而 class 是私有继承;
class 关键字可以用于定义模板参数,就像 typename,而 struct 不能用于定义模板参数

28、Cmake简介

CMake是一种跨平台的构建工具,可以用于自动化构建、测试和打包C++软件。CMake使用CMakeLists.txt文件来描述项目的构建规则和依赖关系,并根据此文件生成适用于不同平台和编译器的构建系统(如Makefile、Visual Studio项目等)。

CMake提供了丰富的命令和变量,用于管理项目的源文件、头文件、库文件、编译选项等,并支持自定义命令和变量,以满足各种构建需求。

CMake还支持构建系统间的交互和集成,例如可以与CTest一起使用进行测试、与CPack一起使用进行软件打包等。

CMake的优点包括:

跨平台支持:CMake可以在多个平台和编译器上生成相应的构建系统,使得项目的移植和跨平台编译变得更加容易。

简洁易学:CMake的语法简洁易学,对于大型项目尤为适用,可以方便地管理源文件、库文件、编译选项等。

强大的扩展性:CMake提供了丰富的命令和变量,并支持自定义命令和变量,使得项目的构建过程可以更加灵活和可控。

集成测试:CMake支持与CTest一起使用进行集成测试,可以方便地管理测试用例和测试结果。

软件打包:CMake支持与CPack一起使用进行软件打包,可以方便地生成各种格式的软件包。
29、什么是野指针?空指针?它们俩的区别?

野指针:指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
产生原因:释放内存后指针不及时置空,依然指向了该内存,可能出现非法访问

30、什么是虚函数?虚函数表存放的内容?

当基类希望派生类定义适合自己的版本,就将这些函数声明为虚函数virtual

虚函数表保存虚函数地址,当用基类指针指向派生类时,虚表指针指向派生类的虚函数表

动态绑定:使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数

何时共享虚函数地址表?
如果派生类继承的第一个是基类,且该基类定义了虚函数地址表,则派生类就共享该表首址占用的存储单元

31、构造函数/析构函数可以是虚函数吗?

构造函数不能是虚函数,并且在构造函数中调用虚函数,实际执行的是父类的对应函数

析构函数可以是虚函数,并且在一个复杂类结构中,这往往是必须的

32、空类编译器自动生成哪些函数?

默认构造函数、默认析构函数、复制构造函数、赋值运算符

当空类被继承时,编译器不会自动生成默认构造函数和默认析构函数,因为派生类的构造函数和析构函数会调用基类的构造函数和析构函数,如果基类没有默认构造函数和默认析构函数,那么派生类就无法实例化。因此,在空类被继承时,必须显式提供构造函数和析构函数。

33、拷贝构造函数的细节?
拷贝构造函数的参数必须是const引用,否则在复制参数时会再次调用拷贝构造函数,导致死循环。

如果类中含有指针类型的成员变量,在拷贝构造函数中需要重新为指针分配内存,并将原来指针指向的数据复制到新分配的内存空间中,避免浅拷贝的问题。

如果类中含有静态成员变量或常量成员变量,则不需要在拷贝构造函数中进行处理,因为这些成员变量是类的属性,而不是对象的属性。

拷贝构造函数在什么情况下会被自动调用?当一个对象被用作函数参数按值传递,或者在函数中返回一个对象时,都会调用拷贝构造函数。

拷贝构造函数是浅拷贝还是深拷贝?如果类中有指针类型的成员变量,则默认拷贝构造函数是浅拷贝,需要自定义拷贝构造函数来实现深拷贝。
34、堆和栈的区别?

堆的空间使用malloc/new和free/delete申请释放,程序员手动管理;
栈是在函数调用过程中用于存储局部变量等的存储空间,由编译器自动生成代码管理;
栈是向低地址扩展的数据结构,是一块连续的内存区域;
堆是向高地址扩展的数据结构,是不连续的内存区域。

栈的内存在哪些地方?哪些地方会用到栈?

栈是一种数据结构,存储在内存中。在计算机中,栈通常被实现为一段连续的内存空间,在运行时会自动分配和释放内存。栈的大小通常是在编译时或运行时确定的,一般由操作系统或编译器负责管理。

栈常常用于存储临时变量和函数调用过程中的局部变量。在函数调用时,函数参数和返回地址等信息会被压入栈中,函数执行时使用栈中的局部变量,函数返回时将栈中的数据弹出,恢复之前的调用现场。此外,栈还可以用于表达式求值、递归函数、缓冲区等场景。

需要注意的是,栈的大小是有限制的,当程序使用的栈空间超过了栈的容量时,会导致栈溢出错误,可能会导致程序崩溃。因此,在编写程序时需要合理地使用栈,避免栈溢出问题的发生。

35、为什么需要虚继承?

虚继承是为了解决多继承时的命名冲突和冗余数据问题(二义性问题),在多继承中,如果两个基类拥有同名的成员函数或成员变量,那么派生类就无法确定应该继承哪一个基类的成员。虚继承可以让派生类只继承一份共同的基类,从而避免在派生类中出现多个拷贝的基类实例。虚继承使得基类在派生类中共享同一个实例,从而消除了二义性。

(C++标准库的iostream就是一个虚继承的例子)

36、什么是范型编程?

泛型编程(Generic Programming)是一种以数据结构和算法为中心的编程方法。其核心思想是编写与特定数据类型无关的代码,使得代码能够适用于多种数据类型,从而提高代码的复用性和可移植性。

在C++中,泛型编程是通过模板(Template)实现的。模板是一种可重用的代码结构,它可以让程序员编写一次代码,而不必为每一种特定类型都编写一遍代码。通过使用模板,程序员可以创建具有通用性的函数、类和数据结构,从而实现真正的代码重用和泛化。

37、请简述private,public,protected,internal的区别?
  • private:私有成员,只能在类的内部访问,外部不能访问
  • public:公有成员,任何地方都可以访问
  • protected:保护成员,可以被类内部以及派生类中访问,但外部不能访问
  • internal:内部成员,只能在当前程序集内部访问
38、C++内存管理?内存分配方式?

大致分为四个区域

代码区:存放CPU执行的二进制代码指令,由操作系统进行管理
全局区:存放全局变量和静态变量以及全局常量和字符串常量
栈区:由编译器自动编译释放,存放函数的参数值,局部变量等
堆区:由程序员分配释放,如果程序员不释放,则程序结束时由操作系统回收

内存分配方式:
a、从静态存储区分配:内存在程序编译的时候就已经分配好,这块程序在程序的整个运行期间都存在,如全局变量,static变量
b、在栈上创建:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限
c、从堆上分配/动态内存分配:程序在运行的时候用malloc或new申请任意多少的内存,程序员负责在何时用free或delete释放内存。

39、如何在主函数之前先执行一段代码?

在C++中,在程序开始执行时会先调用一组称为 “静态初始化” 的函数,这些函数会在程序的 main() 函数之前执行。可以通过在代码中定义一个全局变量来实现在主函数之前先执行一段代码。在定义该全局变量时,在其构造函数中编写需要在主函数之前执行的代码即可。

40、switch参数有什么限制?

switch的参数必须是整型或枚举类型的值。

必须是常量表达式,即其值必须在编译时已知;参数不能是浮点数、字符数组或字符串类型;参数不能是指针类型;不能是表达式或变量。

enum Color { Red, Green, Blue };
//枚举类型的例子
void printColor(Color c) {
    switch (c) {
        case Red:
            std::cout << "红色" << std::endl;
            break;
        case Green:
            std::cout << "绿色" << std::endl;
            break;
        case Blue:
            std::cout << "蓝色" << std::endl;
            break;
        default:
            std::cout << "未知颜色" << std::endl;
            break;
    }
}

int main() {
    Color c = Red;
    printColor(c);
    return 0;
}
41、strcpy和memcpy的区别?
参数类型不同:strcpy 的参数是 char* 类型,而 memcpy 的参数是 void* 类型。

目标内存空间的处理方式不同:strcpy 会在遇到第一个 '\0' 字符时停止拷贝,并把该字符也复制过去,因此适用于字符串的复制;而 memcpy 会完整地复制指定长度的内存块,不会在遇到 '\0' 字符时停止。

安全性不同:strcpy 不检查目标内存空间是否足够大,容易引起缓冲区溢出,从而导致程序崩溃或安全漏洞;而 memcpy 可以指定要复制的长度,可以更加安全地处理内存拷贝。

如果要复制字符串,应该使用 strcpy,而对于其他类型的内存块复制,应该使用 memcpy。在使用 strcpy 时,应当确保目标内存空间足够大,以避免缓冲区溢出等问题。

42、类内static成员可以定义为const的吗?

在 C++ 中,类内的 static 成员变量可以定义为 const。这种情况下,这个静态成员变量的值是编译时确定的,并且在程序运行期间保持不变。

定义为 const 的静态成员变量需要在类定义之外进行初始化

43、什么是智能指针?实现原理?

智能指针是 C++ 中的一个重要概念,它是一个类模板,用于管理动态分配的对象,可以自动释放对象,避免内存泄漏等问题。智能指针包括 unique_ptr、shared_ptr 和 weak_ptr 等类型。

智能指针的实现原理是基于 RAII(Resource Acquisition Is Initialization)技术。当一个对象被创建时,它可以自动地获取一个资源,当这个对象不再需要时,它会自动释放这个资源。智能指针就是利用了 RAII 技术,将动态分配的对象封装在一个对象中,并在对象的析构函数中自动释放这个对象。

44、深拷贝和浅拷贝?

浅拷贝:将一个对象的值复制到另一个对象,这两个对象共享相同的内存地址。当一个对象更改值时,另一个对象也会发生相应的更改。这种拷贝方式默认的拷贝构造函数和赋值运算符都是浅拷贝

深拷贝:在堆中为对象分配新的内存,然后将原对象的值复制到新对象中。新对象和原对象拥有不同的内存地址,修改一个对象的值不会影响到另一个对象。要实现深拷贝,需要自定义拷贝构造函数和赋值运算符

浅拷贝只是简单地复制指针,而深拷贝则是在堆中创建新的内存来存储数据,避免了多个对象共享相同内存的问题。

45、委托构造函数的优缺点?

委托构造函数是C++11新增的特性,可以在一个构造函数中调用同一类的另一个构造函数,从而减少了代码的冗余。

优点:

  • 代码简洁:可以避免代码重复,提高代码可读性和可维护性;
  • 灵活性:委托构造函数可以通过不同的参数调用其他的构造函数,从而实现不同的初始化方式;
  • 安全性:委托构造函数能够确保所有的成员变量都被正确地初始化,避免了忘记初始化的问题。

缺点:

  • 委托构造函数必须在初始化列表中首先被调用,如果没有初始化列表,就不能使用委托构造函数;
  • 委托构造函数不能调用其他非委托构造函数,也不能执行其他的操作,只能用于初始化;
  • 在使用委托构造函数时,需要注意初始化顺序,否则可能会引起不可预知的错误。
46、C++中有几种智能指针?分别有什么异同?

在C++中,智能指针是一种特殊类型的指针,它能够自动释放动态分配的内存,从而避免内存泄漏。C++11引入了三种主要类型的智能指针:unique_ptr、shared_ptr和weak_ptr。

unique_ptr
unique_ptr是一种独占式智能指针,它拥有对其所指对象的独占权,当unique_ptr离开作用域时,它所指向的对象就会被销毁,可以通过std::move()将unique_ptr的所有权从一个对象转移到另一个对象。

shared_ptr
shared_ptr是一种共享式智能指针,可以多个指针共享同一对象,通过计数器来维护资源的引用计数。每当有一个新的shared_ptr指向同一对象时,计数器就会加1,当有一个shared_ptr指向的对象被销毁时,计数器就会减1,当计数器为0时,表示没有任何shared_ptr指向该对象,就会自动释放对象。

weak_ptr
weak_ptr是一种弱引用的智能指针,它可以指向一个由shared_ptr管理的对象,但它不会增加该对象的引用计数,当它所指的对象被销毁后,它将自动变为一个空指针。
47、C 语言中实现死循环有哪些方式?
while(1){}   //1换成 true是同样的效果
for(;;){}
start:
    // 执行代码
goto start;
48、C++标准库map

std map是STL的一个关联容器,map中的元素是关键字----值的对(key–value):关键字起到索引的作用,值则表示与索引相关联的数据。每个关键字只能在map中出现一次。STL的map底层是用红黑树实现的,查找时间复杂度是log(n)

C++标准库中的map是一种关联容器,也被称为字典或关联数组,它提供了一种将键映射到值的方法,其中每个键只能在map中出现一次。它使用一种红黑树的数据结构实现,保证了在log(n)的时间复杂度内进行插入、删除、查找等操作。

map的一些常用特性包括:

它是一个自动排序的容器,它默认按照键进行升序排序,但也可以使用自定义比较函数来进行排序。

它可以存储任意类型的数据,只要支持小于比较运算符(<)即可。

它支持下标运算符[],可以用键作为下标来访问相应的值。

它支持多种迭代器,包括正向迭代器、反向迭代器和const迭代器。

它提供了一些常用的方法,如插入、删除、查找、计数、交换、比较等。

使用map可以实现很多常见的数据处理任务,例如词频统计、排序、去重等。在实际开发中,map也被广泛应用于诸如缓存、路由表、索引等需要高效查找和插入的场景中。

49、友元函数

C++中的友元函数是指在类定义中可以声明为友元的函数。友元函数是可以访问该类的私有成员的非成员函数。

使用友元函数的方式是在类定义中声明该函数为友元函数,一般在类的私有部分声明

class MyClass {
private:
  int my_private_member;
  friend void my_friend_function(MyClass&);
};

友元函数通常用于类之间的协作,可以让某些函数访问另一个类的私有成员,从而减少公有接口,增强程序的安全性。但同时,友元函数也会破坏封装性,需要谨慎使用。

50、define和typedef

define:
只存在简单的字符串替换,没有类型检查;在编译的预处理阶段起作用;可以用来防止头文件重复引用;不分配内存,给出的是立即数,有多少次使用就进行多少次替换

typedef:
有对应的数据类型,要进行判断;在编译、运行时起作用;在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝

51、inline编译限制?

在 C++ 中,inline 是一种关键字,用于告诉编译器将函数的代码插入到函数调用点处,而不是调用函数。这样做可以避免函数调用的开销,提高程序的运行速度。

使用 inline 关键字可以将函数定义为内联函数。内联函数通常被放在头文件中,以便在多个源文件中使用,因为内联函数可以在编译时直接替换调用点,从而避免了在运行时执行函数调用的开销。

在使用 inline 关键字时,需要注意以下几点限制:

递归函数不能定义为内联函数。

函数体不能包含任何循环语句或 switch 语句。

内联函数的定义必须在调用点之前。

编译器可能会忽略 inline 关键字,并将其解释为普通函数。

总之,inline 可以用于优化程序性能,但是需要遵守一些限制,并且不能滥用。只有当函数被频繁调用且函数体简单时,才应该考虑将其定义为内联函数。

(不能存在任何形式的循环语句;不能存在过多的条件判断语句;函数体不能过于庞大;内联函数声明必须在调用函数前)

52、继承

让某种类型的对象获得另一个类型对象的属性和方法

它可以使用现有类的所有功能,并在无须重新编写原来的类的前提下对这些功能进行扩展

方式:实现继承、接口继承、可视继承

C++ Primer Plus中例子:将人定义为一个抽象类,拥有姓名性别年龄等公共属性,吃饭睡觉等公共方法,在定义一个具体的人时,就可以继承这个抽象类,既保留了公共属性和方法,也可以在此基础上扩展跳舞唱歌等特有方法。

53、封装

将数据和代码捆绑在一起,避免外界干扰和不确定性访问

把客观事物封装为抽象的类,并且类可以把自己的数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏

54、多态

多态性允许将父对象设置成为和一个或=更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作

允许将子类类型的指针赋值给父类类型的指针

实现:
override:子类重新定义父类的虚函数的做法
overload:允许存在多个同名函数,这些函数的参数表不同

55、为什么空类的大小不是0而是1?

为了确保两个不同对象的地址不同;类的实例化是在内存中分配一块地址,每个实例在内存中都有独一无二的地址,空类也会实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化后就有独一无二的地址了

若拥有虚函数表的地址或含有指向虚基类的指针,则sizeof为4

56、内存溢出 ?内存泄漏 ?

内存溢出(memory overflow)和内存泄漏(memory leak)都是程序在使用动态内存时的问题,但它们具体的表现和原因是不同的。

内存溢出指的是程序在运行时需要的内存超过了可用的内存大小,导致程序崩溃或异常。常见的内存溢出包括栈溢出和堆溢出。栈溢出通常是由于函数调用层数过多或者局部变量过多导致的;堆溢出则通常是由于使用 new/malloc 分配内存后,没有及时释放,导致内存耗尽。解决内存溢出的方法包括优化程序结构,减少内存使用,增加可用内存大小等。

内存泄漏则是指程序在运行时动态分配的内存没有及时释放,导致内存的使用量逐渐增加,最终导致程序耗尽内存而崩溃。内存泄漏通常是由于程序中存在未释放的指针,或者某些函数在动态分配内存后没有将内存释放,导致内存泄漏。解决内存泄漏的方法包括及时释放动态分配的内存,合理使用 RAII 等机制,对代码进行内存泄漏检测和分析等。

内存泄漏memory leak是指由于疏忽或者错误造成了程序未能释放掉不再使用的内存的情况。并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

当派生类对象中有内存需要回收时,如果析构函数不是虚函数,不会触发动态绑定,只会调用基类析构函数,导致派生类资源无法释放,造成内存泄漏。

57、STL六大组件的交互关系

(容器、算法、迭代器、仿函数、适配器、空间配置器)
a、容器通过空间配置器取得数据存储空间
b、算法通过迭代器存储容器中的内容
c、仿函数可以协助算法完成不同的策略的变化
d、适配器可以修改仿函数

58、析构函数

一个类有且仅有一个析构函数(构造函数可以不止一个);析构函数的名字与类名相同,但要在前面加 ~;析构函数没有参数和返回值;析构函数可以在类内声明,在类外定义(不一定要在类内定义)

59、deque

deque是一个双端开口的连续线性空间,其内部为分段连续的空间组成,随时可以增加一段新的空间并链接

支持快速随机访问,由于deque需要处理内部跳转,因此速度上没有vector快

60、C和C++中的struct有什么不同?

C语言中struct不可以含有成员函数,而C++中的struct可以

61、STL优点
  • 高可重用性
  • 高性能
  • 高移植性
62、解释一下C++中的动态内存分配,并描述new和delete关键字的作用?

C++中的动态内存分配指的是程序在运行时根据需要从系统中分配一块内存,称为堆内存,以供程序使用。动态内存分配可以让程序在运行时灵活地管理内存,解决静态内存分配可能导致的内存浪费和不足的问题。

在C++中,使用new关键字来动态分配内存,new关键字会在堆上分配一块指定大小的内存,并返回一个指向该内存区域的指针。
使用delete关键字来释放内存,delete会将指针所指的堆内存释放,以供其他程序使用。

使用new和delete动态分配和释放内存,可以避免静态内存分配可能导致的内存浪费和不足的问题。需要注意的是,使用new动态分配的内存需要使用delete来释放,否则会导致内存泄漏,即程序无法再次访问该内存。同时,使用delete释放内存后,该内存区域的内容将不再可用,使用该指针访问内存将导致未定义行为。

除了使用new和delete,还可以使用new[]和delete[]来动态分配和释放数组。使用new[]动态分配数组时需要指定数组的长度

使用delete[]释放数组时,需要使用数组名作为参数,而不是数组中的元素指针

63、解释一下C++中的拷贝构造函数和拷贝赋值运算符

拷贝构造函数和拷贝赋值运算符是C++中用来实现对象之间深拷贝的两个重要函数。它们的作用是在对象之间进行数据拷贝,使得每个对象都拥有一份独立的数据副本,避免了对象之间的浅拷贝带来的问题。

拷贝构造函数的作用是在创建一个对象时,以另一个同类对象作为初始化对象来完成新对象的初始化。

拷贝构造函数有一个重要的参数,即同类对象的引用。在使用赋值语句或传递参数时,拷贝构造函数会被自动调用,用以复制数据成员。

class MyClass {
public:
    MyClass(); // 默认构造函数
    MyClass(const MyClass& other); // 拷贝构造函数
};

拷贝赋值运算符是用来重载赋值运算符“=”的函数。它的作用是在已经存在的对象之间完成数据成员的复制,实现两个对象之间的深拷贝。

class MyClass {
public:
    MyClass& operator=(const MyClass& other); // 拷贝赋值运算符
};
64、什么是类和对象?它们之间有什么区别?

在 C++ 中,类是一种自定义的数据类型,可以用来描述一类具有相同属性和行为的对象。类由成员变量和成员函数组成,成员变量描述了对象的属性,成员函数描述了对象的行为。类定义了一种新的数据类型,可以创建该类型的对象。

对象是类的实例化,是类的一个具体实例。对象是类的一个变量,拥有类定义的所有属性和行为。类定义了一种新的数据类型,而对象则是这种数据类型的具体实例。

类和对象的区别可以简单概括为:类是一个模板或者蓝图,而对象是根据这个蓝图创建出来的实体。类是定义,对象是实现。类是一个数据类型,而对象则是该数据类型的一个具体实例。

65、生成可执行文件的四个过程?

生成可执行文件的四个过程如下:

预处理(Preprocessing):在这个阶段,预处理器(Preprocessor)会对源代码进行处理,包括处理 #include 指令、宏展开、条件编译等等。预处理的结果会生成一个新的、具有扩展名为 .i 的文件。

编译(Compilation):在这个阶段,编译器(Compiler)会将预处理后的源代码转换成汇编代码(Assembly code),这个汇编代码是针对特定的处理器和操作系统的。编译的结果会生成一个新的、具有扩展名为 .s 的汇编文件。

汇编(Assembly):在这个阶段,汇编器(Assembler)会将汇编代码转换成可重定位目标文件(Relocatable object file)。这个目标文件中包含了二进制指令、数据、符号表等信息。

链接(Linking):在这个阶段,链接器(Linker)会将多个可重定位目标文件以及库文件(Library)链接成一个单独的可执行文件。链接的过程包括地址重定位、符号解析等操作。最终生成的可执行文件包含了可执行的二进制指令、数据、符号表等信息。

以上四个过程可以一起完成,也可以分开进行。在一些集成开发环境(IDE)中,这四个过程可能会自动地完成,用户只需要点击编译按钮即可。而在一些需要手动编译的环境中,用户需要自己进行这四个过程的每个步骤。

66、声明和定义的区别?

在C++中,声明和定义是两个不同的概念,它们有以下区别:

声明是告诉编译器变量、函数、类等存在,但是不为它们分配内存或者实现代码。定义是在声明的基础上,为变量分配内存或者实现函数、类的代码。

声明只需提供名称和类型,定义则需要给出完整的实现。例如,函数的声明只需要提供函数名和参数列表,而函数的定义需要提供函数体。

声明通常放在头文件中,而定义通常放在源文件中。

在同一作用域中,变量只能定义一次,但是可以声明多次。如果在多个文件中都要使用同一个变量,可以在其中一个文件中定义变量,其他文件中则可以声明该变量。

总结:声明是让编译器知道变量或者函数的类型和名称,而定义则是为它们分配内存或者实现代码。在程序中正确地使用声明和定义是非常重要的。

67、深度可分离卷积、空洞卷积,参数量怎么计算?
68、激活函数,ReLU函数曲线,PReLU函数特点

激活函数是神经网络中的一种非线性函数,它的作用是将神经元的输入值进行非线性变换,引入非线性因素,增加网络的表达能力。激活函数通常被嵌入到神经网络的每个神经元中,用于计算神经元的输出值。即如何把“激活的神经元的特征”通过函数把特征保留并映射出来,即负责将神经元的输入映射到输出端。

引入ReLU的原因:
第一,采用sigmoid等函数,算激活函数时(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。

第二,对于深层网络,sigmoid函数反向传播时,很容易就会出现 梯度消失 的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失),从而无法完成深层网络的训练。

第三,ReLu会使一部分神经元的输出为0,这样就造成了 网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。

ReLU (Rectified Linear Unit) 函数是一种常用的激活函数,其数学表达式为:

f ( x ) = max ⁡ ( 0 , x ) f(x) = \max(0, x) f(x)=max(0,x)

该函数对于所有正数的输入,输出值都等于输入值,对于所有负数的输入,输出值都为 0。因此,ReLU 函数具有简单高效的计算方式和良好的非线性特性,常用于深度神经网络中。

下图是 ReLU 函数的曲线图:
ReLU函数曲线

PReLU (Parametric Rectified Linear Unit) 函数是一种带参数的 ReLU 函数,其数学表达式为:

f ( x ) = max ⁡ ( 0 , x ) + α min ⁡ ( 0 , x ) f(x) = \max(0, x) + \alpha \min(0, x) f(x)=max(0,x)+αmin(0,x)

其中, α \alpha α 是需要学习的参数。PReLU 函数的特点是能够在不同的输入上学习不同的负值斜率,从而提高网络的表达能力和泛化能力。

PReLU 函数相对于 ReLU 函数的曲线更加平滑,能够在一定程度上抑制神经元输出的不稳定性。然而,由于需要学习参数,PReLU 函数的计算量和内存占用要比 ReLU 函数略大一些。

ReLU函数的优势
计算简单快速:ReLU函数的计算非常简单,只需要比较输入值和0的大小关系,因此计算速度非常快。

避免梯度消失问题:在使用sigmoid或tanh等函数时,由于它们在输入值很大或很小的时候梯度会非常小,因此容易出现梯度消失的问题。而ReLU函数的梯度在输入值大于0的时候恒为1,避免了梯度消失的问题。

稀疏性:在输入值小于0的时候,ReLU函数的输出恒为0,因此可以使得网络中的神经元变得更加稀疏。这种稀疏性可以使得网络更加容易训练和泛化。

实现简单:ReLU函数的实现非常简单,只需要进行一个比较运算和一个取最大值的运算即可,因此可以很容易地在硬件上实现。
为什么通常情况下ReLU比sigmoid和tanh强?有什么不同?
梯度消失问题:sigmoid和tanh函数在输入值很大或很小的时候,梯度会非常小,容易出现梯度消失的问题。而ReLU函数在输入值大于0的时候,梯度恒为1,避免了梯度消失的问题。

稀疏性:ReLU函数在输入值小于0的时候,输出恒为0,从而实现了神经元的稀疏性,使得网络中的神经元变得更加独立。而sigmoid和tanh函数则没有这个性质。

实现简单:ReLU函数的实现非常简单,只需要进行一个比较运算和一个取最大值的运算即可,因此可以很容易地在硬件上实现。而sigmoid和tanh函数的实现则需要复杂的指数运算,较难在硬件上实现。
69、weak_ptr原理,为什么能防止两个shared_ptr相互引用?

std::weak_ptr 是 C++11 标准库中的一种智能指针,用于解决 std::shared_ptr 的循环引用问题。

在 std::shared_ptr 中,当两个对象相互引用时,它们之间的引用计数会导致循环引用,即使它们已经不再被程序使用,也无法被自动回收。这种情况下,就会出现内存泄漏的问题。

std::weak_ptr 解决了这个问题。std::weak_ptr 与 std::shared_ptr 类似,但它并不拥有指向对象的所有权。它只是对所指向对象的一个观察者,可以检测到对象是否已被释放,或者是否还存在 std::shared_ptr 指向该对象。

当我们需要在一个类中保存指向另一个类的指针时,可以使用 std::weak_ptr。这样即使两个类相互引用,也不会出现循环引用导致内存泄漏的问题。当 std::shared_ptr 指向的对象不再被使用时,std::weak_ptr 可以被用于检测该对象是否已经被销毁,从而确保程序的内存安全。

具体来说,std::weak_ptr 实现了一个指向被管理对象的弱引用,即它并不会增加对象的引用计数,而是通过 lock() 方法来获得指向对象的 std::shared_ptr。lock() 方法返回的 std::shared_ptr 指向被管理对象,如果对象已经被销毁,则返回空指针。

当一个 std::shared_ptr 被销毁时,它会检查是否有其他 std::shared_ptr 或 std::weak_ptr 引用同一个对象,如果没有,则对象会被销毁,否则对象不会被销毁。

因此,当两个 std::shared_ptr 相互引用时,它们之间的引用计数不会归零,导致对象无法被销毁。但是,当其中一个 std::shared_ptr 转化为 std::weak_ptr 时,它不会增加对象的引用计数,也不会阻止对象被销毁。这样就可以避免循环引用,从而避免内存泄漏的问题。

70、vector中size和capacity的区别?哪个大?

在 C++ 的 vector 中,size() 和 capacity() 都是用来获取 vector 容器中元素数量的函数,但它们的含义略有不同。

size() 函数返回 vector 容器中实际存储元素的个数,也就是当前 vector 中有多少个元素。例如,如果 vector 中存储了 5 个元素,那么 size() 函数将返回 5。

capacity() 函数则返回当前 vector 容器在不重新分配内存的情况下,可以最多容纳的元素个数。当我们向 vector 中添加元素时,如果当前容器的 size() 已经等于 capacity(),那么 vector 将自动重新分配内存来扩大容器的容量。

因此,在 vector 容器中,capacity() 函数返回的值通常大于或等于 size() 函数返回的值,表示当前 vector 容器中还有一些未使用的空间,可以直接向其中添加元素而不需要重新分配内存。

值得注意的是,capacity() 函数返回的是当前 vector 容器的容量,而非可用空间的数量。如果我们想要获得当前 vector 容器中还有多少个元素可以添加而不需要重新分配内存,可以使用表达式 capacity() - size() 来计算。

71、手写transformer,画图、简写公式
72、C++文件读取方式?C?区别?
73、创建进程的函数有哪些?如何改变进程的大小?创建失败怎么办?
74、socket里面read和send的区别?
75、read函数底层实现原理
76、
77、
78、
79、
80、

  • list类型支持双向顺序访问,在list中任何位置插入删除都很快
  • 多态分为三种:模板类和模板函数;函数重载;虚函数
  • 一个类占用一个文件,私有函数和私有变量采用static关键字修饰,即限制了变量和函数的访问范围
  • C++为提高程序运行速度,可将不太复杂的功能用“内联函数”实现
  • AB a(2)相当于调用了一次构造函数,这个构造函数是有参数的
  • AB b[3] 调用了三次构造函数
  • unique_ptr不可复制、赋值,但可以使用move()转换对象所有权,局部变量的返回值除外
  • 函数模板和类模板的参数可以是任意的数据类型
  • 函数默认参数:设定了参数的默认值后,在该参数后定义的所有参数都必须设定默认值
  • 编辑、编译、连接、运行
  • C++中要实现动态联编,必须使用“基类指针”调用虚函数
  • 三大特性:封装、继承、多态
  • C++设置虚基类的目的是:消除二义性
  • C++中的三种传参方式:值传递、指针传递、引用传递
  • 多态分为编译时的多态和运行时的多态;编译时的多态可通过函数重载实现;运行时多态性机制称为动态绑定
  • do…while循环先循环后判断,循环体至少被执行一次
  • 抽象类的声明必须包含abstract关键字;抽象类只能用作其他类的基类;不能使用抽象类定义对象;抽象类不能用作参数类型、函数返回类型或显式转换的类型
  • ”函数代码少、频繁调用“适宜采用inline定义函数
  • & 按位与操作只有对应两个二进制数都为1,结果才为1
  • ”动态分区分配“方案要求程序在主存必须连续存放
  • OOP是重要的设计思想,不要求具体语言,C++只是比C语言更适合于OOP编程
  • 局部变量的作用范围仅仅在定义它的方法内,或是在定义它的控制流块中
  • 返回值不能作为区别函数重载的标志
  • C++中,来自class的继承默认按照private继承处理,来自struct的继承默认按照public处理
  • C语言里的struct只是变量的聚合体,struct不能有函数
  • C++的struct可有构造和析构函数
  • 一个.cpp文件会生成一个.obj文件,.h文件不会生成.obj文件
  • 一个参数既可以是const同时是volatile(希望它是只读的,同时是易变的)
  • 在C++中,含有纯虚函数的类称为抽象类,它不能生成对象,抽象类必须被继承且纯虚函数被覆盖后,由子类实例化对象
  • ||或逻辑:左边为真,就会忽略右边表达式;&&与逻辑:左边为假,就会忽略右边表达式
  • 函数名(实参1,实参2,实参3 ,… ,实参n) 实参可以是具有确定值的常量、变量、表达式、函数等
  • fopen、exit函数的调用必须进入内核才能完成
  • 虚函数用于实现运行时的多态,或者称为晚绑定或动态绑定,虚函数可以声明为inline
  • 静态函数不可以是虚函数
  • 二目运算符重载为类成员函数时 由于this指针的作用,函数参数会少一个
  • vector在堆中分配了一段连续的内存空间来存放元素
  • continue是结束本次循环,本次循环中continue下面代码不再执行,直接进入下次循环。break,直接跳出当前循环。
  • 析构函数的格式:~类名(); 没有参数列表、返回值类型及返回值。
  • 封装的主要作用在于对外隐藏内部实现细节,增强程序的安全性
  • 在函数体中,一个被声明为静态的变量在这一函数被调用过程中只会被分配一次内存,且整个运行期间不会重新分配
  • C++不同于C语言,他有更严格的类型检查,参数不写返回类型,不会默认为int
  • strcpy拷贝函数不安全,可能会导致内存溢出,它不做任何的检查措施,也不判断拷贝大小,不判断目的地址内存是否够用
  • memcpy拷贝函数与strcpy的区别就是memcpy可以拷贝任意类型的数据,strcpy只能拷贝字符串类型
  • map的迭代器的key是const类型,无法对其进行修改
  • list是双链表实现,插入的元素的复杂度为O(1)
  • 递归过程的局部变量过多、递归深度过大,是造成系统栈溢出的原因
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SmallC1oud

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值