12:00起床
13:20-14:25英语学习
14:25-16:30算法学习,要不要找方法
c++
static
修饰普通变量
存储在静态区,生命周期随程序,具有全局性、共享性、所有人可见。
内存分配和释放是由编译器和链接器在编译和链接阶段自动处理的,而不是由程序员显式管理。
修饰普通函数
表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。
修饰成员变量
修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。
修饰成员函数
修饰成员函数使得不需要生成对象就可以访问该函数,但是在 static 函数内不能访问非静态成员。
c++ 变量的存储区域
自动存储区(Automatic Storage Area)
栈区(Stack Area):用于存储局部变量和函数参数。在函数调用时分配内存,函数返回时释放内存。具有自动分配和释放的特性。栈上的变量是按照后进先出(LIFO)的方式管理。
寄存器变量(Register Variables):声明为
register
关键字的变量,请求编译器将其存储在 CPU 寄存器中,以便快速访问。对于大多数现代编译器,register
关键字已经被忽略,编译器会自动进行优化。
静态存储区(Static Storage Area):
静态区(Static Area):用于存储全局变量、静态变量和静态对象。在程序的整个生命周期内存在,由编译器和链接器在编译和链接阶段进行内存分配和初始化。静态区的变量具有全局可见性和共享性。
动态存储区(Dynamic Storage Area):
堆区(Heap Area):用于手动分配和释放内存,通常用于动态对象的创建和管理。在堆上分配的内存需要手动释放,否则可能会导致内存泄漏。
自由存储区(Free Store):与堆区类似,是通过
new
和delete
运算符进行内存分配和释放的区域。自由存储区是堆区的一部分,两者之间的术语通常可以互换使用。
#pragma pack(n)
设定结构体、联合以及类成员变量以 n 字节方式对齐
#pragma pack(n) 使用
#pragma pack(push) // 保存对齐状态
#pragma pack(4) // 设定为 4 字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop) // 恢复对齐状态
extern "C"
被 extern 限定的函数或变量是 extern 类型的
被
extern "C"
修饰的变量和函数是按照 C 语言方式编译和链接的
extern "C"
的作用是让 C++ 编译器将 extern "C"
声明的代码当作 C 语言代码处理,可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
extern "C" 使用
#ifdef __cplusplus
extern "C" {
#endif
void *memset(void *, int, size_t);
#ifdef __cplusplus
}
#endif
静态多态(编译期/早绑定)
函数重载
class A
{
public:
void do(int a);
void do(int a, int b);
};
动态多态(运行期期/晚绑定)
虚函数:用 virtual 修饰成员函数,使其成为虚函数
动态绑定:当使用基类的引用或指针调用一个虚函数时将发生动态绑定
注意:
可以将派生类的对象赋值给基类的指针或引用,反之不可
普通函数(非类成员函数)不能是虚函数
静态函数(static)不能是虚函数
构造函数不能是虚函数(因为在调用构造函数时,虚表指针并没有在对象的内存空间中,必须要构造函数调用完成后才会形成虚表指针)
内联函数不能是表现多态性时的虚函数,解释见:虚函数(virtual)可以是内联函数(inline)吗?
动态多态使用
class Shape // 形状类
{
public:
virtual double calcArea()
{
...
}
virtual ~Shape();
};
class Circle : public Shape // 圆形类
{
public:
virtual double calcArea();
...
};
class Rect : public Shape // 矩形类
{
public:
virtual double calcArea();
...
};
int main()
{
Shape * shape1 = new Circle(4.0);
Shape * shape2 = new Rect(5.0, 6.0);
shape1->calcArea(); // 调用圆形类里面的方法
shape2->calcArea(); // 调用矩形类里面的方法
delete shape1;
shape1 = nullptr;
delete shape2;
shape2 = nullptr;
return 0;
}
虚析构函数
虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。
虚析构函数使用
class Shape
{
public:
Shape(); // 构造函数不能是虚函数
virtual double calcArea();
virtual ~Shape(); // 虚析构函数
};
class Circle : public Shape // 圆形类
{
public:
virtual double calcArea();
...
};
int main()
{
Shape * shape1 = new Circle(4.0);
shape1->calcArea();
delete shape1; // 因为Shape有虚析构函数,所以delete释放内存时,先调用子类析构函数,再调用基类析构函数,防止内存泄漏。
shape1 = NULL;
return 0;
}
纯虚函数
纯虚函数是一种特殊的虚函数,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
virtual int A() = 0;
数据结构
并查集
int len = 1000;
int pre[len];
int find(int c) { // 函数返回 c 的根节点
int r = c;
while(parents[r] != r) { // 返回根节点 r
r = parents[r];
}
int i = c, j;
while(i != r) { // 压缩算法,将每个节点的父节点更新为根节点
j = parents[i];
parents[i] = r; // 更新为根节点 r
i = j;
}
return r;
}
void union(int node1, int node2) { // 判断 node1 和 node2 是否连通,如果不连通那么将其所在分
int root1 = find(node1); // 支进行合并。
int root2 = find(node2);
if(root1 != root2) {
parents[root1] = root2; // 合并:这里 root1 和 root2 的顺序可以不考虑
}
}
二叉树
完全二叉树(堆)
大顶堆:根 >= 左 && 根 >= 右
小顶堆:根 <= 左 && 根 <= 右