1. 命名空间
1.1 使用方式:
作用域限定符:wd::number
using 编译指令:using namespace std;
问题:using编译指令可能会造成冲突,这是因为using编译指令它会把该空间中的所有实体一次性全部引入
using声明机制:using std::cout; // 只会引入这一个实体,推荐使用
1.2匿名的命名空间
什么是模块?一个*.c/*.cc/*.cpp的文件就可以称为一个模块
只能在本模块内部使用的是什么?不能跨模块使用的是什么?
C语言中,static变量 static函数都只能在本模块内部使用,外部无法引入
C++中,匿名命名空间的实体也是只能在本模块内部使用
全局变量就可以跨模块调用 extern关键词
补充static知识
修饰变量
普通局部变量(存储于进程栈空间,使用完会立即释放):在任何一个函数内部定义的变量都属于这个范畴。编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值
静态局部变量(存储于进程的静态数据区,即使函数返回,它的值也会保持不变):使用static修饰符定义的局部变量,即使在声明时未赋初值。编译器也会把它初始化为0
普通全局变量:对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其名字相同的变量了(负责编译器会认为他们是同一个变量)
静态全局变量:仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用
修饰函数
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数
静态成员函数只能使用静态数据成员
命名空间是否只可以定义一次?
函数声明可以有多次,但定义只有一次
同一个模块中,可以定义多次命名空间;
在不同的模块中,也可以定义多次命名空间
命名空间就像一个容器(黑洞)可以无限定义实体
2.const关键字
修饰类型: char/short/int/long
const int b = 2;// 不能再修改的值,常量
宏定义与const常量有什么区别?
发生的时机:宏定义是在预处理时;const常量是在编译时
类型检查:宏定义没有类型检测,只是简单做了字符串的替换,虽然也有编译阶段,但在编译阶段没有报错,将出错的时机延迟到了运行时。运行时的错误更难发现;const是有类型检查的,更安全一些。
const修饰指针
常量指针(pointer to const):const int *p1 = &a; int const *p2 = &a;
不能修改的是指针所指空间的值
所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。
可以这样想:所谓指向常量的指针和引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,所以自觉的不去改变所指对象的值。
指针常量(const pointer):int *const p3 = &a;
不能修改指针变量的指向
3、new/delete表达式
3.1 C语言中动态申请堆空间的资源,采用的是malloc/calloc
3.2 C++中也提供了类似的方式,new表达式/delete表达式
int *pint = new int(1);// new表达式申请空间的同时,也进行了初始化
delete pint; // 释放堆空间
int *parr = new int[10];
delete [] parr;
3.3 malloc的底层实现?new表达式底层实现?
free(p);// 如何判断该回收多少空间?
在C中,free 函数并不需要显式地知道要释放多少空间。当你调用 free(ptr) 时,ptr 是一个指向动态分配内存块的指针,free 将释放 ptr 指向的整个内存块。
这是因为malloc在内部通常会在分配的内存块的起始部分存储一些额外的信息,其中包括分配的大小。free则使用这个大小信息来确定要释放的空间
malloc 使用了一种内存池(memory pool)的机制
在程序启动时,malloc 会初始化一个内存池。这个内存池一般由一个或多个堆(heap)组成。
堆是进程内的一块大内存区域,由操作系统分配给进程。malloc 使用堆来存储动态分配的内存块。
当调用 malloc(size) 时,malloc 会在内存池中寻找一个足够大的、未被使用的内存块。
如果找到了足够大的内存块,malloc 将这个内存块标记为已分配,并返回一个指向这个内存块的指针。
这个过程可能涉及内存块的拆分(splitting)或合并(coalescing),以便更好地利用内存。
3.4 malloc/free 与new/delete表达式的区别是什么?
malloc和free是C语言的函数,它们只关心分配和释放内存,不涉及构造和析构对象
new和delete是C++的运算符,除了分配和释放内存外,它们还会调用对象的构造函数和析构函数,处理对象的生命周期
new能够自动分配空间大小,malloc需要传入参数;
new开辟空间的同时还对空间做了初始化的操作,而malloc不行;
3.5 内存泄漏工具valgrind的使用
命令较长,可以起一个别名放在~/.bashrc文件中:alias memcheck='valgrind --tool=memcheck --leak-check=full'
接下来再执行source.bashrc
3.6 new 表达式工作步骤
调用名为operator new的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象:void* operator new(size_t size){ return malloc(size)};
运行该类型的一个构造函数初始化对象
返回指向新分配的构造函数对象的指针
3.7 delete 表达式工作步骤
调用析构函数,回收对象中数据成员所申请的资源,也就是malloc申请的堆空间
调用名为operator delete的标准库函数释放该对象所用的内存
4. &符号的理解
引用&:对变量取一个别名
int a = 1; int & ra = a;
取地址 &:int a = 1; int *p = &a;
按位与 &:int c = a & b;
引用可以作为函数的参数:
经典例子,交换两个变量的值
对比使用指针的好处是: 没有赋值的开销,可以提高程序的执行效率
引用的底层实现其实还是指针,而且是一个受限制的指针
引用作为函数的返回值
类型的变量:在return语句中直接返回一个变量,要执行的是复制
指针类型:只做了一个地址传递,效率很高,也是进行了复制的
引用类型:代表的就是变量本身,没有复制开销
5. 强制转换
5.1 static_cast
常见的指针转换(把void *转换成其他类型的指针) 用的最多。
5.2 const_cast
去除常量属性,用法很诡异
5.3 dynamic_cast
只用在多态时,基类与派生类之间的转换
5.4 reinterpret_cast
不要轻易使用,在任意类型之间进行转换,平时用的最少
6.函数重载
6.1 C语言不支持函数重载
C语言中如果有新的接口时,会用数字来重新命名
正是因为C语言不支持函数重载,所以在C语言中,函数名就代表了函数的入口地址
而C++支持函数重载,所以函数的地址最好使用函数名加上取地址符号&
6.2 什么叫函数重载(overload)?
函数名相同,但参数列表不同:函数参数的类型、个数、顺序不同(没有函数返回值类型)
6.3 C++实现函数重载
实现原理: 名字改变(name mangling)
具体步骤: 当函数名相同时,会根据函数参数的类型、个数、顺序不同进行改变
验证:g++ -c overload.cc 得到overload.o文件,使用nm overload.o命令查看
7. C语言和C++的混合编程
7.1 背景
C语言写的库已经遍地开花,C++需要调用C的库,必须要能按照C的方式进行调用。
希望使用C++的编译器来编译C的源码
7.2 解决方案
extern "c"{}
只要放在该区域的代码,都会按照C的方式进行调用,不会进行名字改编
搭配宏可以完美解决
#ifndef __ADD_H__
#define __ADD_H__
#ifdef __cplusplus
extern "C"
{
#endif
int add(int x, int y )
{
return x + y;
}
#ifdef __cplusplus
}
#endif
#endif
8.内存分布
这里面有错误,char char2[] = "abcd"; 这里的"字符串不是字面值常量",不是放在代码段的,而是放在栈中的(可以进行修改)