C++基础学习
文章目录
参考:
- https://blog.csdn.net/luanfenlian0992/article/details/118771472#t13
- https://zhuanlan.zhihu.com/p/335994370
编译内存相关
编译
编译.cpp .h
过程分为四个过程:编译(编译预处理、编译、优化.s
),汇编.o
,链接。
静态链接
动态链接
变量与内存分区
内存分区
-
栈区:
正常函数体内{}"
定义的都是。
连续内存。 -
堆区:
new
出来的。
类似链表的分段内存。 -
全局/静态存区:
static
和全局变量extern
. -
常量区:
常量。 -
代码区:
编译后的二进制文件。
变量类型
-
全局变量:
放在.cpp
文件里面的,不再{}
内的变量,同样的名称多文件共享。
多个cpp
文件共用或者{}
内使用需要extern
修饰声明。 -
静态全局变量:
static
修饰,具有文件作用域,多个文件同样的名称是不同的变量。 -
局部变量:
{}
里面定义的。 -
静态局部变量:
static
修饰的{}
函数中的变量,具有函数作用域,只初始化一次。
内存对齐
对齐存取快,不报异常好迁移。
有些设备不支持非对齐的存取,不能保证原子操作。
内存泄露
new
出来的内存,由指针进行操作,后来没delete
指针指向别的了,之前的那块内存地址就找不到了,没法删除啥的。
智能指针
智能指针,自动释放内存,防止内存泄露。
-
shared_ptr
多个指针共享一块内存,通过引用计数自动释放。 -
unique_ptr
一个指针独占一块内存,不能拷贝赋值。可以移动构造和移动赋值构造。 -
weak_ptr
指向 shared_ptr 指向的对象,能够解决由shared_ptr带来的循环引用问题。
include “” 和<>
https://www.iteye.com/blog/kooyee-340846解释:
<>
先去系统目录中找头文件,如果没有在到当前目录下找。所以像STL
标准的头文件 stdio.h、stdlib.h
等用这个方法。
而""
首先在当前目录下寻找,如果找不到,再到系统目录中寻找。 这个用于include自定义的头文件,让系统优先使用当前目录中定义的。
语言对比
c++ 11
自动类型推导
auto
auto var = val1 + val2; //定义+初始化为左边的值
decltype
decltype(val1 + val2) var1 = 0; //定义+初始化为其他值
lembda 表达式 (匿名函数)
[capture list/*捕获的局部变量列表*/] (parameter list/*形参*/) -> return type/*返回变量类型*/
{/*具体函数实现*/function body;
};
范围for
for (declaration /*列表中的一个元素*/: expression/*列表、数组*/){statement
}
这玩意不和python的for差不多吗,多了个declaration的变量类型常用auto
。
右值引用 与 std::move()
右值引用可以修改右值,借助 std::move()
转换引用左值
int &&a = 10;
a = 100;
int b = 5;
int &&refb=std::move(b);
https://zhuanlan.zhihu.com/p/335994370总结的右值引用好处
- 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。
- 右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。
- 作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性。
delete 函数和 default 函数
delete 函数:= delete
表示该函数不能被调用。default 函数:= default
表示编译器生成默认的函数,例如:生成默认的构造函数。
#include <iostream>
using namespace std;class A
{
public:A() = default; // 表示使用默认的构造函数~A() = default; // 表示使用默认的析构函数A(const A &) = delete; // 表示类的对象禁止拷贝构造A &operator=(const A &) = delete; // 表示类的对象禁止拷贝赋值
};
int main()
{A ex1;A ex2 = ex1; // error: use of deleted function 'A::A(const A&)'A ex3;ex3 = ex1; // error: use of deleted function 'A& A::operator=(const A&)'return 0;
}
对比C、python
- C++:面向对象
- C:面向过程
- 没有
bool
类型,用int
代替
- 没有
- python:解释执行
面向对象
特性
- 封装:将对象的数据和过程绑定在一起则被称为封装。
- 继承
- 多态:子类重写父类的
virtual
虚函数实现。
重载、重写、隐藏的区别
-
重载:
就是函数的重载,和面向对象没关系 -
隐藏:
子类的同名函数会隐藏父类的同名函数,如果需要调用父类函数需要.Base::fun()
-
重写(覆盖):虚函数,纯虚函数(子类一定要重写)
父类的函数有virtual
修饰,函数名、参数列表、返回值类型需要一致。
主要应用:父类指针指向new
子类对象,实现父类指针调用子类的函数。
关键字和库函数
这玩意太多了还是直接看别人写的吧
explicit
:避免编译器进行隐式类型转换static
:- 修饰类内的变量函数时候,可以不创建类对象直接使用,不能定义为
virtual
函数。只能访问static
成员。因为静态成员函数没有 this 指针。 - 静态数据成员的类型可以是所属类的类型,而普通数据成员的类型只能是该类类型的指针或引用。
- 修饰类内的变量函数时候,可以不创建类对象直接使用,不能定义为
const
:修饰常量不能更改,只能初始化时候赋值。mutable
???#define
:在编译预处理阶段进行文本替换typedef
:定义类型的别名inline
:内联函数。delete
、delete[]
new
、malloc
、delete
、free
:malloc、free 是库函数,而new、delete 是关键字new
:内存分配成功,返回该对象类型的指针;分配失败,抛出 bac_alloc 异常。申请空间时,无需指定分配空间的大小,编译器会根据类型自行计算;申请空间时,返回的类型是对象的指针类型,无需强制类型转换,是类型安全的操作符;malloc
:成功申请到内存,返回指向该内存的指针;分配失败,返回 NULL 指针。在申请空间时,需要确定所申请空间的大小。申请空间时,返回的是 void* 类型,需要进行强制类型的转换,转换为对象类型的指针。- 对于自定义的类型,new 首先调用 operator new() 函数申请空间(底层通过 malloc 实现),然后调用构造函数进行初始化,最后返回自定义类型的指针;delete 首先调用析构函数,然后调用 operator delete() 释放空间(底层通过 free 实现)。malloc、free 无法进行自定义类型的对象的构造和析构。
- new 操作符从自由存储区上为对象动态分配内存,而 malloc 函数从堆上动态分配内存。(自由存储区不等于堆)
- malloc 的原理?malloc 的底层实现?
- C 和 C++ struct 的区别?为什么有了 class 还保留 struct?
union
:联合体,只有一个有效的成员,对联合体的不同成员赋值,将会对覆盖其他成员的值,联合体的大小为其内部所有变量的最大值,按照最大类型的倍数进行分配大小。- class 和 struct 的异同
- class(private 继承),struct(public 继承)
- class 可以用于定义模板参数,struct 不能用于定义模板参数。
volatile
:多线程巴拉巴拉的,类似线程锁。extern
:C 语言编写的函数- sizeof(1==1) 在 C 和 C++ 中分别是什么结果?
- memcpy 函数的底层原理?
- strcpy 函数有什么缺陷?
14.-17.啥的从来没用到过QAQ
类相关
虚函数 与 纯虚函数
- 虚函数必须实现,否则编译器会报错;
- 包含纯虚函数
virtual fun()=0;
的类为抽象类不能实例化对象。 - 子类继承抽象类,没有完全重写纯虚函数,也是抽象类。
虚函数实现机制
类的对象存储指向虚函数表的指针实现
- 虚函数表
-
虚函数表存放的内容:类的虚函数的地址。
- 虚函数表建立的时间:编译阶段,即程序的编译过程中会将虚函数的地址放在虚函数表中。
- 虚表指针保存的位置:虚表指针存放在对象的内存空间中最前面的位置,这是为了保证正确取到虚函数的偏移量。
-