剑指offer复盘(二)——C++语言基础
按照剑指offer顺序,第二章内容是关于基础知识的部分。以下主要是关于C++语言的内容总结,主要是针对之前有些不太了解的地方,作一下简略的记录,可能主观性较强,想到哪里就写到哪里,后续会不断补充。与STL和刷题有关的知识会放在另外的地方。
关于const
const与指针
const int * p和int * const p的功能呢可以通过结合顺序来理解,前者是常量指针(指向常量的指针变量),指向const int类型,可以修改指针指向的变量但是不能进行(*p=1)操作,后者是指针常量(一个常量),指向int类型,可以进行(*p=1)操作不能进行(p+=1)操作。
const与引用
首先看一下引用,引用是一个变量的别名,不占用内存,引用必须被初始化,并且初始化后不可以再修改为指向其他变量。
注意初始化的时候并不是用被引用变量的地址,而是直接使用被引用变量。
int a = 1; int &r = &a; //错误 int &r = a; // 正确
const引用可以使用不同的类型进行引用初始化,其相当于是绑定并认为该被变量/表达式是const的。需要注意的是,即使是中间需要进行类型转换(如double到const int &),或者绑定的是临时变量,const引用也是有效的。
那么这样的操作有什么用呢?显然,普通的引用的功能是通过这个别名来访问/修改所被引用变量的值:
int a = 1; int &r = a; r = 2;
下面具体到使用场景上,常见的const引用使用场景是作为函数参数进行传递,比如:
int func(const int & a);
此时,我们不能通过引用a修改对应变量的值。
在函数中,为了减少创建pass-by-value创建变量的消耗,所以使用pass-by-reference,但是在pass-by-reference时,又不希望子函数去修改传入变量,这种情况就可以使用const引用来达到目的。
const与类成员
const修饰类成员函数时,可以防止这个类成员函数修改类中的成员变量;
-
int A::fun(int x) const{ XXX; }
同时,只有const类成员函数才能访问const类成员变量。
为什么要这样设计呢?个人认为主要是出于封装的考虑,比如一个类Student可能会含有private成员变量unsigned int age,那么如果外界想要访问学生Student的年龄,可以定义一个成员函数unsigned int Student::get_age() const;,通过这样一个const成员函数去获取成员变量的值,避免了暴露内部成员变量。
关于static
static关键字的使用主要有以下几种情况:
static静态局部变量
静态局部变量是在函数内部创建的静态变量,特点是:
- 数据位于全局存储区
- 首次执行函数的时候被初始化,然后值一直保持,下一次调用函数的时候不再初始化;
- 会进行默认初始化
- 作用域依然是局部的,仅在函数内可以用
static静态全局变量
全局变量定义在函数外,使用static主要是为了实现文件隔离,static的变量只能在本文件内被使用(区别于全局变量)。
static静态函数
与静态全局变量相似,也可以文件隔离。
static静态类成员变量
静态成员是属于类本身,而不是每次实例化一个类就产生新的变量。
static静态类成员函数
思想和上面是一样的,静态成员函数不能访问非静态的成员函数和非静态数据成员,因为它是属于类本身的。
关于强制类型转换
首先要理解什么是类型转换,类型转换是指从一种数据类型转换到另外一种数据类型(废话),类型转换有显式explicit和隐式implicit之分,比如一个函数接受参数为double类型,但是传入为int,这个时候会进行隐式类型转换(int到double):
double fun(double a){ XXX; return b; } int x = 10; double y = fun(x);
而显式类型转换是通过括号()操作,直接把一个变量的类型转化为另一个类型,比如:
double a = 3.14; int b = (int) a;
以下主要讨论显式类型转换,C语言中对于显式类型转换比较放得开,只要把目标类型扔进括号()就可以(此处不绝对,处理指针要麻烦一些,暂不涉及)。但是在C++中,除了兼容C的这一做法外,更好的选择是使用4个与类型转换有关的关键字进行转换:
- static_cast
- dynamic_cast
- const_cast
- reinterpret_cast
个人认为,按照以下方法记忆比较好:
static_cast主要用于C++中内置的基本数据类型之间的转换,也可以用于派生类到基类指针/引用之间的转换,但是有以下两种限制:
- 不用于基类到派生类之间的转换
- 不能用于添加/删除const限定
为了解决限制1,可以使用dynamic_cast,dynamic_cast可以完成基类到派生类之间指针/引用的转换,其可以进行动态类型检查,因此称为dynamic。
为了解决限制2,可以使用const_cast,const_cast主要用于从指针/引用类型中加入/去除const或者volatile限定。
此外,还剩下reinterrpret_cast,其相对比较万能,可以转化任何内置类型为其他类型,也可以把任何类型指针转化为其他的类型,因为正如名字一样它进行了重解释reinterpret。这个操作并不安全,因此它也不常用。
关于OOP概念
OOP的核心概念是继承、封装、多态。以下均为个人理解:
继承
为了提高开发效率,进行代码复用,可以将对象类进行组织,使得对象类的公共性质/功能被提炼出来作为一个类,后面的每个对象对应的类共同享有这一性质。
封装
隐藏具体实现方法,提供接口,为开发创造方便。
多态
有了继承和封装之后,我们想要实现特异性,即不同类对同一功能有不同的做法。这就是多态。
C++多态主要通过虚函数实现,在基类中定义一个函数为虚函数,后面的派生类可以根据自己的需要进行不同的实现。
在实现方法上,拥有虚函数的类拥有虚函数指针。虚函数按照表的方式进行组织,构成虚函数表。在子类中,如果函数覆盖了对应的虚函数,其在虚函数表的对应位置也发生了对应替换,这样完成了多态的实现。
C++内存划分
堆、栈、静态存储区、常量区、代码区
全局变量使用
可以用引用头文件的方式,也可以用extern关键字。用引用头文件方式,如果变量出错,则在编译期间会报错;如果用extern方式,假定犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
类中访问限制
private是私有类型,只有本类中的成员函数、友元访问;protect是保护型的,本类和继承类可以访问;public是公有类型,任何类都可以访问。
智能指针
智能指针存在于<memory>头文件中,主要有shared_ptr、unique_ptr、weak_ptr。
- shared_ptr中多个智能指针可以共享同一个对象
- unique_ptr中指针独占对象
- weak_ptr共享但不拥有某对象,用于打破环状引用
内存对齐
需要注意:
- 每一个成员的偏移量都必须是该成员的倍数
- 结构体的大小必须是该结构体字节数最大成员的倍数
TodoList
-
构造/析构函数的处理
-
运算符/函数重载
参考资料
https://blog.csdn.net/weixin_38403778/article/details/82290139
https://blog.csdn.net/weixin_43329614/article/details/89103574
https://www.cnblogs.com/junlinfeizixiao/p/6193412.html
https://www.cnblogs.com/chio/archive/2007/07/18/822389.html
https://www.cnblogs.com/suomeimei/p/10513522.html
https://blog.csdn.net/weixin_40311211/article/details/82851300
https://blog.nowcoder.net/n/f73dc0635ae14a7e8643a9dba9b5783c

浙公网安备 33010602011771号