一、基础知识
1. 名称空间
-
产生原因:
厂商将代码封装成产品,但是两个厂商的函数有重名的情况发生,会产生误解
-
解决办法:
A公司将其产品放到名为A的名称空间中;B公司则放到B的名称空间中。这样就可以采用如下方法进行区分
A::Run(); B::Run();
-
std:
类、函数、变量等都是C++编译器的标准组件,都被放在std名称空间中。所以其实可以省略using而用
std::cin >> a;
等进行代码编写,然而这样面对大项目还是会出现问题,因此我们用using std::cin; using std::cout; using std::endl;
等来包含一些可用函数(不用打命名空间名称std)
2. cin和cout
-
概念:
-
输出是一个流,<<运算符的作用是将右边的东西插入到流中
-
输入也是一个流,>>运算符从输入流中抽取字符
-
<<和>>符号用来表示信息流的方向
-
endl确保程序继续运行前刷新输出,而‘\n’没有这样的保证,意味着有时输入信息后才会出现提示
-
-
以两个下划线或下划线和大写字母开头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符
-
大括号初始化
int a{};
或者int b = {};
,如果没有数据,则会初始化为0 -
更改输出进制
cout << oct; cout << hex; cout << dec; cout.setf(ios_base::boolalpha); // 输出的值将转化为布尔值
-
读取整行带空格不带回车的字符时使用
cin.getline(name, size);
(string数组改其中值内存地址咋变的?——string实质上是一个指针,指向首字母,所以string的size都是32位,而且数组里string地址连续) -
raw字符串:
string R"(asdaffergrgr)";
只显示括号内的内容,完全照搬,引号括号之间可以自己加东西定义边界
3. 结构体
-
制定占用特定位数的结构成员,创建与某个硬件设备上的寄存器对应的数据结构方便,可以使用没有名称的字段来提供间距
struct torgle_register{ unsigned int SN : 4; // 4 bits for SN value unsigned int : 4; // 4 bits unused bool goodIn : 1; // valid input (1 bit) bool goodTorgle : 1; // successful torgling };
-
共用体:能储存不同数据类型,但只能同时储存一种(可以放在结构体里面),常用于节省内存
union one4all{ // 去掉one4all是匿名共用体,成员为位于相同地址处的变量,因此需要标识符,表明 int i; // 哪个变量正在活动 long l; double d; };
-
枚举(enum):创建符号常量,可以代替const
enum colors{red, orange, blue};
(colors可省略)作用是创建类似结构体的colors,把大括号中名称均作为常量,赋值0~2,常用于定义符号常量(switch)enum colors{red, orange, blue}; // 仅有赋值运算符,但可以单向转换为int colors c; c = red; // valid c = 2000; // invalid enum {g = 1, kg = 1000, t = 1000000, unknown}; // unknown = 1000001
-
计算结构体大小的规则(字节对齐):
-
每一个成员的偏移量都必须是该成员的倍数。
-
结构体的大小必须是该结构体字节数最大成员的倍数。
-
4. 指针和自由存储空间
-
为什么用指针?因为指针快啊!一个地址才多大!
-
在创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存!
int* fellow; // 未分配地址,可能有任何值 *fellow = 233; // 野指针出现了!
-
面试常见题
new/delete和malloc/free的区别
- 参数
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
返回类型
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void* ,需要通过强制类型转换将void*指针转换成我们需要的类型。
分配失败
new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
自定义类型
new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
- 重载
C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。
内存区域
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。
-
delete会删除指针指向的内容,但是不会删除指针。一定要配对使用new和delete,否则会发生内存泄漏,也就是分配的内存再也无法使用了
-
一个已经释放的内存块不能再被delete;delete只能释放new出来的东西;delete空指针是安全的。
常见错误(瞿老师常说的):两个指向同一内存块的指针被delete,如string等号赋值后delete两个,解决办法:先申请内存,再用strcpy/strncpy// strncpy用法 char food[20]; strncpy(food, "asdasdasdasdasdasdasdasdasdasdasd", 19); food[19] = '\0';
-
sizeof(pArray);
指针的大小,sizeof(array);
数组的长度 -
如何打印char类型指针的地址而不是打印出字符串来?将其强制转化为其它类型的指针
cout << (int *) pChar;
-
自动存储、静态存储和动态存储
-
自动存储
在函数内部定义的常规变量使用自动存储空间,被称为自动变量,在函数调用时产生,结束时消亡。自动变量是一个局部变量,通常存储在栈中,后进先出。程序执行时,栈会不断增大缩小,内存是连续的
-
静态存储
整个程序执行期间都存在的存储方式,定义方法是全局变量或static
-
动态存储
new和delete管理了一个内存池,被称为自由存储空间或堆,该内存池与静态变量和自动变量的内存是分开的,所以我们可以在一个函数中new,另一个函数中delete。也是因此,占用的自由存储区可能不连续
-
栈、堆和内存泄漏
如果在自由存储空间(或堆)上new出来的变量没有delete,会怎么样?
即使包含指针的内存由于一些原因被释放,自由存储空间内分配的变量也将继续存在
我们将无法访问这些结构,因为指向这些内存的指针无效
这将导致内存泄漏
被泄露的内存在程序的整个生命周期内都不可用,也无法被收回(退出程序后系统基本会清理)。如果内存耗尽或是占用了其它在这些内存空间中运行的应用程序,则会崩溃
5. 数组的替代品
-
数组
a1[-2] = 20;
是允许的,按内存空间分配,数组的效率最高 -
vector
虽然同样允许,但是可以用
a2.at(3)
语句来禁止,代价是效率变低另外,vector对象本身在栈中,而vector中的数据存放在堆中
-
array
长度固定、使用栈(静态内存分配)、效率与数组相同但是更安全
#include<array> ... array<int, 5> ai; // 不能用变量长度创建