C++基础知识整理

C++基础[持续更新]

1.指针和引用的区别

1.指针是一个变量,本身占内存(32/64位),这段内存里的数据为一个地址;引用跟原来的变量为同一个东西,编译器层面的别名

2.有const指针,没有const引用(本身变不可修改指向和指向地址的内容)

3.指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)

4.指针的值可以为空,但是引用的值不能为nullptr,并且引用在定义的时候必须初始化

5.指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了

6."sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小

7.指针和引用的自增(++)运算意义不一样;

2.堆和栈的区别

在这里插入图片描述
栈:自动,申请快,根据系统,通常较小,使用过多会溢出;
堆:手动,申请慢,内存条大小,链表->内存碎片;

3.new和delete是如何实现的,new 与 malloc的异同处

new底层调用 :

	void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
    {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                        _THROW_NCEE(_XSTD bad_alloc, );
                }
  
        return (p);
    }

简单类型直接调用operator new分配内存,复杂类型还要再执行构造函数;
通过new_handler来处理new失败的情况;
new分配失败的时候不像malloc那样返回NULL,直接抛出异常;要判断是否分配成功应该用异常捕获的机制;

delete底层调用:

	void operator delete( void * p )
	{
	    RTCCALLBACK(_RTC_Free_hook, (p, 0));
	  
	    free( p );
	}

复杂类型还需先执行析构函数;

new/delete底层还是最终执行了malloc/free;对于new[]/delete[],复杂类型还会额外记录数组大小

同异

  • new/delete是C++关键字,需要编译器支持;malloc和free是库函数,需要头文件支持
  • 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸
  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void* ,需要通过强制类型转换将void*指针转换成我们需要的类型。 C++允许重载new/delete操作符,malloc不允许重载
  • new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中
  • 内存泄漏对于new和malloc都能检测出来,而new可以指明是哪个文件的哪一行,malloc确不可以

4.Struct和class的区别

从语法上来讲,class和struct做类型定义时只有两点区别

  • 默认继承权限:如果不明确指定,来自class的继承安置private继承处理,来自struct的继承安置public继承处理。
  • 成员的默认访问权限:class的成员默认private权限,struct默认public权限

大部分class有的,struct也可以实现

5.define 和const的区别(编译阶段、安全性、内存占用等)

  • const变量必须在定义时初始化,而且类中的const变量必须在初始化列表中初始化;

  • const在编译阶段起作用,可以进行调试。#define在预编译阶段起作用,简单替换,不能进行调试

  • const有类型检查,#define无类型检查,const安全程度高

  • 编译器不为const变量分配空间而是将其存储在符号表中读取效率高占用内存少,define每次用到都要分配空间,占用内存大,效率低

  • const不能被重新定义,而#define可以通过#unde后进行重新定义

  • #define还可以用来防止头文件被重复包含问题

6.在C++中const和static的用法(定义,用途)

const:
1.修饰指针 *;在 *号前表示指针指向的地址中的值不能修改;在 *号后表示指针指向的地址不能修改
2.修饰函数返回值;则这个函数返回值作为左值时编译会报错(相关内容:函数返回值的引用)
3.修饰类;类中const成员变量与普通const变量区别不大;初始化 const 成员变量只能通过参数初始化表;const成员函数不能修改任何成员变量;const 对象在该对象生命周期内,必须保证没有任何成员变量被改变,const对象只能调用const成员函数。

static:
1.修饰非类成员;隐藏、全局生存期(早于main函数,静态存储区,默认值为0)
2.修饰类成员变量;在类中的静态数据成员变量属于类共享,即使没有定义对象,静态成员也存在,静态数据成员在类中说明,在类外定义、初始化,并分配内存空间,一经定义则必须初始化,可以通过对象去访问静态成员,类名::静态成员,整个程序的生命周期
3.修饰类成员函数;静态成员函数属于一个类而不是某个对象(没有this指针),不能是虚函数,不能直接访问非静态成员变量( 虚函数和非静态成员变量是对象创建的时候才会有的)

7.const和static在类中使用的注意事项(定义、初始化和使用)

1.static成员变量(非const)必须在类外定义,在类中只是作为声明,不能使用类初始化成员列表来初始化,只能在定义的时候初始化;const的成员变量必须在类中定义的时候就初始化,不能在类外再定义,不能使用类初始化成员列表初始化。

2.static 与 virtual不能共存于一个函数上,即没有静态虚函数,原因在于静态函数没有this指针,无法通过对象实例来调用,而虚函数与普通成员函数一样必须通过this指针来调用。但是static成员可以通过类实例或类来调用,即“A a;a.b”或“A::b”都可。

4.非static的const成员,和引用类型的成员,因为在定义的时候就必须初始化,所以必须使用类初始化成员列表来初始化。

5.在一个函数的最后加上const,则这个函数只能是一个成员函数,而且是非静态的。因为const的函数就隐含着“this指针所指的对象不能被修改”这个意思。同时,const函数不能对任何成员函数做赋值等修改的动作,而且如果一个实例是const实例,这个实例只能调用const函数。

6.static成员在编译的时候就已经确定了(未显式初始化的则为0)。而普通成员则在创建实例的时候初始化。

故一个类对象里面的成员的初始化顺序:

程序加载时确定:
a.静态成员变量的空间及值。
b.成员函数的空间及地址。
创建对象时(运行时)确定:
c.虚函数表(如果该类或该类的任一个基类,超类有虚函数)
e.普通成员变量的空间及值。

8.C++中的const类成员函数(用法和意义),以及和非const成员函数的区别

通常一个类的某些属性会被定义为私有变量以达到保护和隐藏的目的,但是恰好外部需要获取这个变量的值时,可以使用const成员函数返回私有变量的值,可读不可写。

9.C++的顶层const和底层const

其实就是const指针和指针const

const int *p1;//等价int const *p1;   底层const
int * const p1;//顶层const

主要记住,const右边先是*号还是先是变量名,哪个先就限定哪个

10.final和override关键字

override用于当一个继承自A类的B类需要重写A类的函数时,显示的告诉编译器,该函数为“继承重写”;防止由于笔误等情况,将fun();写成fnu();导致并没有重写而是产生了一个新函数的情况。

final类似java中,用于当一个类或者一个函数不想再被其他类继承或继承后重写时,显示的告诉编译器不可继承或重写;

举个例子,java中的string类是由fianl修饰的,不可继承,而c++早期没有final,string类可以继承,这就导致了*************

11.拷贝初始化和直接初始化,初始化和赋值的区别

12.extern "C"的用法

13.C++中的重载和重写的区别

14.C++多态的实现

15.C++虚函数相关(虚函数表,虚函数指针),纯虚函数,虚函数的实现原理(包括单一继承,多重继承等)(拓展问题:为什么基类指针指向派生类对象时可以调用派生类成员函数,基类的虚函数存放在内存的什么区,虚函数表指针vptr的初始化时间)

16.C++中类的数据成员和成员函数内存分布情况

17.this指针

18.析构函数一般写成虚函数的原因

19.构造函数、拷贝构造函数和赋值操作符的区别

20.构造函数声明为explicit;构造函数为什么一般不定义为虚函数;

21.构造函数的几种关键字(default delete 0)

22.构造函数或者析构函数中调用虚函数会怎样

23.静态类型和动态类型,静态绑定和动态绑定的介绍

引用是否能实现动态绑定,为什么引用可以实现

24.C++所有的构造函数

25.深拷贝和浅拷贝的区别(举例说明深拷贝的安全性)

对象复用的了解,零拷贝的了解

什么情况下会调用拷贝构造函数(三种情况)

26.结构体内存对齐方式和为什么要进行内存对齐

27.C++的四种强制转换

28.decltype()和auto

29.inline和宏定义的区别

30.模板函数和模板类的特例化

31.C++的STL源码(内存池机制,各种容器的底层实现机制,算法的实现原理等)

STL中vector的实现

STL源码中的hashtable的实现

STL中unordered_map和map的区别和应用场景

STL容器的几种迭代器以及对应的容器(输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器)

STL中的traits技法

vector使用的注意点及其原因,频繁对vector调用push_back()对性能的影响和原因

手写智能指针的实现(shared_ptr和weak_ptr实现的区别)

智能指针的循环引用

成员初始化列表的概念,为什么用成员初始化列表会快一些(性能优势)

内存泄露的定义,如何检测与避免?内存泄露的定义,如何检测与避免?

内存检查工具的了解

遇到coredump要怎么调试

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A: C11标准主要对以下方面进行了改进: 1. 原子操作:C11添加了一套原子操作API,用于在多线程应用程序中执行原子操作,防止竞争条件。 2. 多线程:C11定义了一组库函数,用于创建、共享和同步线程的操作。 3. 静态断言:静态断言是编译时断言,而不是运行时断言,它能够在编译时检查程序的有关属性是否符合预期。C11中添加了_Static_assert关键字,用于实现静态断言。 4. Unicode 字符串支持:C11提供了一组库函数,用于在处理Unicode字符串时使用的字符和字符串I / O。 5. 安全和boundaries:C11在库和语言层面上增强了安全性和下界检查,防止缓冲区溢出等常见错误。 6. Type Generics:C11对C语言添加了一些新的“泛型”特性,使得在编译时根据特定的类型来生成代码更加容易。 7. 对齐和 _Alignof:C11增加了一个包含 _Alignof 运算符, 该运算符返回请求的类型的自然对齐方式的大小 。这无疑帮助程序员更好的理解程序的性能问题。 8. remove 指针:C11提供了一个新的关键字 _Generic,它可以根据输入表达式的类型自动选择正确的代码块,从而方便地处理不同类型的指针。它还通过添加 remove关键字来解决泛型指针的一些问题,remove 关键字将指针的类型“去掉”,只保留其中的基本类型。 9. 原子类型支持:C11增加了对 _Atomic 的支持,允许应用程序对特定的变量或数据结构强制要求原子访问。 10. 无名联合体:C11允许在联合体中定义无名成员。这使得程序员可以创建大小和类型不可用的联合体,无需分配任何内存。 以上是C11的主要特性和改进,包括了原子操作、多线程、静态断言、Unicode支持、安全和边界、Type Generics、对齐和_Alignof、remove指针、原子类型支持、无名联合体等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值