C++ 杂记
本文最后更新于:2024年12月22日 凌晨
00
基础
内存分区
- 代码区:函数体的二进制代码
- 全局区:全局变量、静态变量、常量
- 栈区:函数调用、局部变量,编译器自动分配
- 堆区:程序员动态分配内存,如new和delete
枚举
enum enum_name{a,b,c..} var_name;
枚举标识符可赋值,默认从0开始+1
const
常类型的变量或对象的值无法被更新
修饰变量
必须赋初始值,且赋值之后无法修改
与#define相比,const定义的常量有数据类型,编译器可以进行类型安全检查,而#define只是单纯的字符串替换
可以防止修改,提高代码健壮性;同时节省空间
const变量默认为文件作用域,如果想在其他文件中使用,需要加extern
修饰指针
int const *p;
或 const int *p;
: const在*左边,无法通过指针修改这个变量的值指向常量的指针
无法通过这个指针修改,但仍然可以通过变量名、引用,或者其他指针修改
int * const p
: const在*右边,指针指向的地址不能被修改 常指针
定义时必须初始化,常指针无法修改指向的地址,即使两个变量指向同一块地址
修饰引用
同理,不能通过引用修改值,但仍可以通过变量名修改
修饰函数返回类型
函数返回值时,加const无意义,因为本身返回的值也会赋给其他变量,该值就可以通过其他变量修改
返回指针时同上
修饰函数形参同上
修饰类、对象、成员函数
const对象只能访问const成员函数,const成员函数可以访问所有成员变量和其他const成员函数,但无法修改
static
为什么引入static:函数内部定义的变量,程序执行到时才分配内存到栈区,函数运行结束即释放。但有时候想要保存该变量的值到下一次调用,同时又不想改变该变量的访问范围
修饰成员变量
在程序启动时就被创建,不依赖类的对象,可以使用类名来直接调用
修饰成员函数
同样不依赖于类的对象,无法使用this指针,只能访问静态成员变量/函数
struct和class
区别:struct的默认访问和继承权限是public,而class的默认访问和继承权限是private
define和typedef
define只做单纯字符串替换,没有类型检查,在预处理阶段起作用;typedef相当于类型别名,在编译运行时起作用
new和malloc
new是一个操作符,使用时会初始化一个对象,调用构造函数,返回一个指向新分配空间的对象实例的指针,因此无需指定内存大小,使用完毕指针销毁时并不会自动回收内存,因此需要手动delete来调用对象的析构函数释放
malloc是一个库函数,使用时仅仅显式的在堆上分配指定大小的内存
面向对象
构造函数
编译器默认为每个对象提供空的构造函数和析构函数,以提供对象初始化和清理功能,也可以自定义
class_name(){}
可以有参数,可以重载,程序调用对象前自动执行
编译器默认会给一个类添加三个函数:默认构造函数、拷贝构造函数、析构函数
如果定义了有参构造,编译器不再提供默认构造,需要手动定义;同理,如果要自定义拷贝构造函数,编译器也不会再自动生成其他构造函数
析构函数
~class_name(){}
不能有参数,无返回值,程序结束时自动调用
深拷贝和浅拷贝
浅拷贝:只是简单的拷贝,指针指向的地址相同,两个对象指向同一块内存,释放一个对象的内存会导致另一个对象的内存无效
深拷贝:重新在堆区分配一块内存,将原对象的值拷贝到新的内存中,两个对象指向不同的内存
对于拷贝构造来说,如果类中有属性是在堆区开辟的(比如说被拷贝的对象中有指针,指针指向堆区new出来的一块内存),那么在拷贝时也需要重新在堆区开辟内存,并将原对象中的值拷贝到新的内存中
1 |
|
如上,m_height本身是指针,构造时在堆区创建一块内存并指向该内存。如果在拷贝时仅仅做m_height = p.m_height,会导致两个对象指向同一块内存,释放一个对象的内存会导致另一个对象的内存无效
构造与析构顺序
继承
基类构造->派生类构造->派生类析构->基类析构
成员对象
成员对象构造->外层对象构造->外层对象析构->成员对象析构
this指针
类的每个成员函数只会诞生一份函数实例放入代码区,多个同类型的对象会共用这份代码
在调用成员函数时,C++内置this指针,用于指向调用该函数的对象
友元friend
修饰全局函数
将全局函数在类中声明为友元,就可以访问该类的私有成员
修饰类
将类A在类B中声明为友元,A就可以访问B的私有成员
修饰成员函数
将类A的成员函数在类B中声明为友元,A的成员函数就可以访问B的私有成员
继承
无论怎么继承,都无法访问从父类继承的私有成员。
私有成员还是会继承,但只是被隐藏了
公共继承 class A : public B
父类中的公共和保护成员类型不变
保护继承 class A : protected B
父类中的公共和保护成员变为保护成员
私有继承 class A : private B
父类中的公共和保护成员变为私有成员
多态
如果有多个派生类且需要给他们分别实现一个同名的行为,比如说一个动物类,有狗、猫、猪等派生类,都有一个叫的行为,但是叫的实现不同,这时候就可以使用多态
多态的必须条件:父类指针指向子类对象,其实就是该指针将这个子类对象当作一个其父类,因此可以调用父类中的虚函数,同时由于指向的是子类对象,所以调用的是子类中重写的函数
虚函数 virtual
在基类中,将需要多态的函数以virtual关键字声明,并在派生类中重写该函数。此后,若是父类指针指向子类对象时,并使用该指针调用多态函数,就会调用子类的函数
virtual的意思类似“这个函数可能会被子类重写”,告诉编译器先不急着确定函数地址,编译器会根据指针指向的对象来动态调用对应的函数
纯虚函数
在需要使用多态的场景中,往往基类中的虚函数只是为了让派生类重写,并无实际意义,此时可将虚函数改为纯虚函数
1 |
|
数组
1 |
|
栈
1 |
|
队列
1 |
|
双向队列
1 |
|
哈希表
1 |
|