C++问答汇总_2023自用

news2024/11/17 0:50:22

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、

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 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、


  • 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)
  • 递归过程的局部变量过多、递归深度过大,是造成系统栈溢出的原因

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/366442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis的安装部署和配置文件的修改

1、准备安装环境 由于 Redis 是基于 C 语言编写的&#xff0c;因此首先需要安装 Redis 所需要的依赖&#xff1a; yum install -y gcc tcl gcc-c make 2、上传安装文件 将下载好的 redis-6.2.7.tar.gz 安装包上传到虚拟机的任意目录&#xff08;一般推荐上传到 /usr/local/s…

贝叶斯网络实践

目录 一。朴素贝叶斯的假设 二。朴素贝叶斯的推导 三。高斯朴素贝叶斯Gaussian Naive Bayes 四。多项分布朴素贝叶斯Multinomial Naive Bayes 五。以文本分类为例 1.分析 2.分解 3.拉普拉斯平滑 4.对朴素贝叶斯的思考 六。总结 七。word2vec 八。GaussianNB,…

【数据结构】Map 和 Set

目录二叉搜索树二叉搜索树---查找二叉搜索树---插入二叉搜索树---删除Map和SetMap的使用Set的使用哈希表哈希冲突冲突避免冲突解决冲突解决---闭散列冲突解决---开散列题目练习只出现一次的数复制带随机指针的链表宝石与石头旧键盘二叉搜索树 二叉搜索树也叫二叉排序树&#x…

(二十六)大白话如何从底层原理解决生产的Too many connections故障?

今天我们继续讲解昨天的那个案例背景&#xff0c;其实就是经典的Too many connections故障&#xff0c;他的核心就是linux的文件句柄限制&#xff0c;导致了MySQL的最大连接数被限制&#xff0c;那么今天来讲讲怎么解决这个问题。 其实核心就是一行命令&#xff1a; ulimit -H…

分布式面试题

目录 分布式id的生成方案有哪些 雪花算法生成的ID由哪些部分组成 分布式锁在项目中有哪些应用场景? 分布式锁有哪些解决方案 Redis做分布式锁用什么命令 Redis做分布式锁&#xff0c;死锁有哪些情况&#xff1f;如何解决 Redis如何做分布式锁 MySQL如何做分布式锁 什么…

代码签名即将迎来一波新关注

在数字化高度发展的当下&#xff0c;个人隐私及信息安全保护已经成了大家关注的重点&#xff0c;包括日常使用的电脑软件&#xff0c;手机APP等&#xff0c;由于包含了大量的用户信息&#xff0c;已经成了重点关注对象&#xff0c;任何一个疏忽就可能泄露大量用户信息。所以权威…

了解线程安全

线程安全是多线程的重点和难点。 线程安全概念 线程安全&#xff1a;在多线程的各种随机调度顺序下&#xff0c;代码没有bug&#xff0c;都能够符合预期的方式来执行&#xff0c;此时认为线程安全 线程不安全&#xff1a;如果在多线程随机调度下代码出现bug&#xff0c;此时…

Java Web:开篇综述与第一章

前言 翻开这本书&#xff0c;又是一段新的学习路线&#xff0c;在学习的道路上是枯燥的&#xff0c;是乏味的&#xff0c;难免有放弃的想法。但回看曾经的学习笔记&#xff0c;自己也一步一步走过来了&#xff0c;即使会自我怀疑自我否定&#xff0c;但不坚持不努力是永远没有…

#G. 求约数个数之六

我们先求到区间[1..b]之间的所有约数之和于是结果就等于 [1..b]之间的所有约数之和减去[1..a-1]之间的约数之和很明显这两个问题是同性质的问题&#xff0c;只是右端点不同罢了.明显对于1到N之间的数字&#xff0c;其约数范围也为1到N这个范围内。于是我们可以枚举约数L,当然这…

【ROS学习笔记1】ROS快速体验输出Hello World

【ROS学习笔记1】ROS快速体验输出Hello World 文章目录【ROS学习笔记1】ROS快速体验输出Hello World1.1 ROS快速体验1.1.1 Hello World快速实现简介1.1.2 Hello World的C实现1.1.3 Hello World的Python实现写在前面&#xff0c;本系列笔记参考的是AutoLabor的教程&#xff0c;具…

求职3个月,简历大多都石沉大海,一听是手工测试都纷纷摇头....太难了

距离被上家公司裁员已经过去了3个月了&#xff0c;3个月的求职经历真的让我痛不欲生&#xff0c;我也从中理解感叹到了很多&#xff0c;想写出来&#xff0c;告诫跟我一样的经历的人。 我今年26岁&#xff0c;大学是一所普通的大专&#xff0c;学的是机电专业&#xff0c;如何…

【Django】内建用户、文件上传、发送邮件、项目部署

一、内建用户系统 Django带有一个用户认证系统用来处理账号、cookie等 from django.contrib.auth.models import User1、创建用户 from django.contrib.auth.models import User # 普通用户 user User.objects.create_uer(username用户名,password密码,email邮箱) # 超级用…

这几个免费、商用图片素材网,你一定要知道

很多朋友不知道去哪里找图片素材&#xff0c;找到了又担心会不会侵权。 今天给大家分享7个免费可商用图片素材网站&#xff0c;这下再也不用担心找不到素材或侵权啦&#xff01; 1、菜鸟图库 传送门&#xff1a;美女图片|手机壁纸|风景图片大全|高清图片素材下载网 - 菜鸟图库…

hive只复制表结构不复制表数据

目录 一、背景 二、准备测试数据 1.建表 2.造测试数据 三、操作 1.CTAS &#xff08;1&#xff09;.无分区表测试 &#xff08;2&#xff09;.分区表测试 2.LIKE &#xff08;1&#xff09;.无分区表测试 &#xff08;2&#xff09;.分区表测试 一、背景 有一张ori_…

《狂飙》壁纸大嫂如此惊艳,做成日历壁纸天天看

兄弟们&#xff0c;今年的反腐大剧狂飙都有看吗 &#xff1f; 话说&#xff0c;名字虽然叫狂飙&#xff0c;但是全剧只有有田一个人在狂飙&#xff01; 当然&#xff0c;有田虽然亮眼&#xff0c;但是毕竟是个糟老头子&#xff0c;正经人谁看有田啊&#xff0c;当然是看大嫂了…

【在 Colab 中使用 TensorBoard 绘图】

【在 Colab 中使用 TensorBoard 绘图】进入 Google Drive进入 Colab在深度学习中&#xff0c;使用本机GPU跑可能会比较慢&#xff0c;这里使用 Google Drive Colab 进行训练&#xff0c;运行代码 进入 Google Drive 进入网盘 初次进入需要注册账号。注意科学上网即可。右键…

路由器防火墙配置(14)

实验目的 通过本实验&#xff0c;理解路由器的防火墙工作原理&#xff0c;掌握路由器的防火墙功能配置方法&#xff0c;主要包括网络地址转换功能和数据包过滤功能的配置。 培养根据具体环境与实际需求进行网络地址转换及数据包过滤的能力。 预备知识网络地址转换 网络地址转…

SSIM学习

SSIM原文链接&#xff1a;https://www.researchgate.net/profile/Eero-Simoncelli/publication/3327793_Image_Quality_Assessment_From_Error_Visibility_to_Structural_Similarity/links/542173b20cf203f155c6bf1a/Image-Quality-Assessment-From-Error-Visibility-to-Struct…

Window 的 PHP XAMPP 安装 mongodb 的扩展

需要安装的扩展为&#xff1a;extensionphp_mongodb.dll根据官方的指引&#xff1a;PHP: Installing the MongoDB PHP Driver on Windows - Manual 1需要到 GitHub 上下载扩展&#xff0c;然后进行安装。这里的版本选择有些讲究。首先1.51 是 mongoDB 的驱动版本号&#xff0c;…

Bulletproofs++

1. 引言 前序博客&#xff1a; Bulletproofs: Short Proofs for Confidential Transactions and More学习笔记Bulletproofs 代码解析Bulletproofs: Shorter Proofs for Privacy-Enhanced Distributed Ledger学习笔记Bulletproofs 代码解析 Liam Eagen 2022年3月论文《Bullet…