1.static的作用
- 修饰全局变量或者函数的时候,会改变他们的外部连接属性,将他们的作用域限制在当前源文件内部,外部文件无法通过extern关键字来引用该变量或者函数。
- 修饰局部变量,局部变量会存放在静态区中,变量的生命周期变为全局性的,在出作用域之后也不会被销毁,会保留上一次操作结束的值。
- 在C++的类中修饰成员变量,会将该变量变为属于整个类,而并非某个对象了,所有对象共享这个静态成员变量。
- 在C++的类中修饰成员函数,该函数也会变为属于整个类,修饰之后的函数没有隐藏的this指针,只能访问静态成员变量和静态函数。
2.栈区与堆区作用与区别
栈区作用:
存储函数内部定义的局部变量和函数传递的参数,当函数被调用的时候,这些局部变量都会被分配内存,函数调用结束之后,又会将这些占用的内存自动释放掉。还会保存函数的一些调用信息,例如函数调用的返回指针,栈帧指针等等。这些信息用于函数调用结束后能正确的返回到调用的位置。
堆区作用:
主要用于动态的内存分配,提供给程序员在程序运行的时候根据需要动态的申请和释放内存的区域。C中提供malloc、calloc、realloc、free等进行操作,在C++当中使用new和delete运算符进行操作。
区别:
- 栈区的内存分配和释放是系统自动完成的,而堆区的内存分配和释放操作需要我们手动完成。
- 栈区的内存分配效率会相对较高一些,栈区的内存分配和释放操作时通过移动指针完成的,所以会非常快,而堆区的话需要查找合适的空闲内存块、更新管理的内存分配表等等操作,所以销毁会低很多。
3.重写重载重定义
函数重载:在同一作用域当中允许存在同名的函数,但是函数的参数个数、类型以及类型顺序必须有一个不同才可以,否则无法分辨调用的是哪一个函数。对于返回值不同也不可以作为函数重载的标准。
函数重定义:也叫隐藏,当子类中定义了一个与父类同名的非虚函数的时候,父类的同名函数就会被隐藏,调用的时候会先调用子类的函数,并非是覆盖了,因为可以显示的调用父类的同名函数。只要函数名相同就可以构成重定义。
函数重写:也叫覆盖,当派生类定义了一个虚函数的时候,子类也定义了一个函数名、参数列表以及返回类型都一样的函数的时候,就实现了重写。重写的函数会覆盖底层的函数地址。
4.gcc/g++常用选项
选项 | 说明 |
-c | 只进行编译,生成.o二进制文件,不进行链接操作 |
-o | 指定输出文件的名称,不指定的话默认是a.out文件 |
-s | 只进行编译生成汇编文件 |
-g | 编译时生成调试信息,方便进行程序的调式 |
-l | 指定要连接的库文件 |
-L | 指定库文件的路径 |
-I(大写i) | 指定头文件的搜索路径 |
-D | 在编译的时候可以定义宏 |
-stdxx | 用于指定C++的标准版本 |
5.C语言与C++的区别
C语言是一种面向过程的编程语言,注重解决问题的步骤和过程。而C++是在一种面向对象的编程语言,引入了类、对象、继承和多态的概念,更加注重数据的封装和对象的操作。在语法上来说的话,引入了布尔类型以及引用的概念。在函数参数的传递上,提供了默认参数的概念,对于构造函数和析构函数可以帮我们自动的完成初始化和释放资源的操作。在内存管理上看new和delete也会自动调用构造和析构函数。还是C++还配套了STL标准库以及模板等内容,让代码更加的间接,编程更加方便。
语法:类、对象、封装继承多态、6个默认函数、引用、模板、STL容器等。
6.智能指针
智能指针是用于管理动态分配内存的一个工具,通过智能指针的生命周期来管理动态分配的内存的生命周期,当智能指针析构的时候,符合特定条件的话就会将动态管理的内存释放掉。
智能指针 | 说明 |
auto_ptr | 最早的智能指针,他只会允许统一时间只有一个智能指针指向同一个对象,当发生赋值或者拷贝的话,所有权就会转移,就相当于该指针不在管理对象了。 |
unique_ptr | 和auto_ptr一样,但是他不允许赋值和拷贝操作。会将拷贝构造函数和赋值重载函数禁用。 |
shared_ptr | 是共享所有权的一个指针,他允许多个智能指针指向同一个对象,他内部使用引用计数来记录个数,当引用计数为0的时候,才会去释放指向的对象资源。 |
weak_ptr | 大多数是用来配合shared_ptr进行使用的,解决了shared_ptr的循环引用的问题,他没有对象的管理权,不参与引用计数的增减。 |
weak_ptr和shared_ptr之间的相互转化:
可以直接使用shared_ptr去构造weak_ptr,或者通过赋值的方式。而weak_ptr本身不直接拥有对象,所以转化为shared_ptr的话,需要先换取对象,所以内部提供了一个lock()函数帮助我们去实现该操作。lock()函数返回的就是一个shared_ptr对象。
7.左值引用与右值引用
左值
左值是表示一个对象的表达式,他有持久的内存地址,一般是出现了赋值语句的左边。通俗来说就是可以取地址的表达式都是左值。例如:变量、数组元素等。
右值
右值是一个临时的即将被销毁的表达式,他没有持久的地址,通常出现在赋值语句的右边。例如:字面常量,临时对象,函数的值返回等。
左值引用
使用&符号来声明左值引用,可以延长所引用对象的一个生命周期,只要引用存在的话,该对象就不会被销毁。左值引用只能引用左值对象,如果想引用右值的话,就必须变为const引用才可以。
作为函数参数的话,可以减少值得拷贝,提高性能。作为函数的返回值,返回对象的引用,也会减少拷贝,同时可以在函数外面修改对象的值。
右值引用
使用&&符号来声明右值引用。同样只能绑定到一个右值上面。右值引用主要是实现移动语义和完美转发,右值引用也会延长右值的生命周期,在右值引用的生命周期内,该右值都不会被销毁的。右值引用不能绑定到左值上,但是可以通过move将左值强制转换为右值后进行绑定。
右值引用的意义
对于左值引用的话,已经解决了大部分的拷贝效率问题了,为什么还要右值引用呢?例如下面的场景,调用了一个函数,返回一个string对象,同时用一个string对象去接收该对象的值,那么就会发生一次拷贝构造和一次赋值重载,这两个操作是无法被优化的。但是我们又知道,只要把返回的值直接给接收的对象就好了,没必要拷贝了两次数据。
使用移动拷贝的话,就不需要拷贝了,而是把将亡值的内容,通过改变内部指针来把数据直接交换到临时对象当中,然后使用移动赋值,在将数据转移到接收的对象当中,这样就不需要拷贝数据了。这样的话就把将亡值的内部数据利用了起来,不需要再创建一个临时对象拷贝给左值了。
如果说使用const左值引用接收的话,也可以会减少一次拷贝,但是他指向的是一个临时变量的右值,那么就无法去修改操作该值了,但是移动赋值的话是将数据交换到左值当中,那么左值就可以进行随意修改了。