更多精彩请关注微信公众号“爱折腾的码农”,二维码见下图。
本篇内容主要总结本人在秋招过程中总结的一些c++相关资料,包括、static、const、内存分配(new/molloc)、sizeof等。
- const
一、修饰普通变量
1、利用const进行修饰的变量在程序的任意位置都不能被修改,如同常数一样,若修改则会导致编译错误;
2、由于常量被定义后就不能修改,因此在定义时必须初始化,这点和引用相同。
3、非const变量在其他源文件中引用的话直接加上extern关键字即可,本文件中只需定义即可, 但是对于const变量在引用的原文件及定义的源文 件中都要加上extern。
非const属性变量
(定义源文件)int num = 0;
(引用源文件extern int num;
const属性变量
(定义源文件)extern const int num = 0;
(引用源文件extern const int num
二、修饰指针
常量指针:指针指向变量的值不可通过该指针修 改,但是指针变量的值可以改变; 例:const int* num
指针常量:指针变量不允许修改,但该地址里的内容可以被修改;
例:int* const num
三、修饰引用变量
1、非常量引用不能绑定在常量上;
2、常量引用既可以绑定在常量上也可以绑定在非常量上;
3、不能通过常量引用去改变被引用的值。
四、修饰成员函数
1.不能修改成员变量和调用非const成员函数(静态成员变量和静态成员函数除外)否则会报错;
2.在函数的声明处末尾和实现处末尾都要加const关键字;
3.可以通过在函数名最后加上const实现重载;
例:void fun(){}
void fun() const{}
4.const 关键字不能与static关键字同时使用,因为static关键字修饰静态成员函数, 不含有 this 指针,不属于具体的对象,只属于 类本身,但是const 成员函数必须具体到某一 实例。
- static
一、修饰静态变量
1.初始化的静态变量在数据段,未初始化的全局变量在.bss段;
2.未初始化的静态全局变量会被程序自动初 始化为0(自动变量的值是随机的,除非它被显 式初始化);
3.静态全局变量和静态函数在声明它的整个文件都是可见的,而在文件之外是不可见;
4.静态局部变量同样具有全局性,在函数结束时不会消失,每次该函数调用时也不会 为其重新分配空间。它始终在全局数据区,直到程序运行结束.
二、修饰静态函数
static函数与普通函数最主要区别是static 函数在内存中只有一份,普通静态函数在每个被调用中维持一份拷贝程序的局部变量存在于(堆/栈)中,全局变量存在于(静态区)中,动态申请数据存在于(堆)。
三、修饰静态成员变量
1.不管定义多少个类对象,静态数据成员都共 享分配在全局数据区的内存,所以节省存储空间;
2.一旦某变量需要改变时,只要改变一次,则 所有对象的该变量全改变;
3.有一些状态是和类本身相关的而不是和对象 相关的 这些状态数据可以用静态成员变量去表达.
四、修饰静态成员函数
大体功能类似于静态成员变量,但是有个注意点:静态成员函数只能调用静态成员变量。这主要是因为this指针的问题,对于普通成员函数和成员变量来说,其属于对象(每个对象都有一个隐式this指针指向它的首地址),而静态成员函数 属于类本身,因此,不存在this指针的概念。
扩展知识:this指针(面试中也会问到)
为什么会有this指针?
在类实例化对象时,只有非静态成员变量属于对象本身,剩余的静态成员函数、静态成员变量、非静态函数都不属于对象本身,因此非静态成员函数只会实例一份,多个同类型对象共用一块代码,由于类中每个实例后的对象都有独一无二的地址,因此不同的实例对象调用成员函数时,函数需要知道是谁在调用它的,因此引用this指针。
来源网络
this指针的作用
this指针是隐含在对象成员函数内的一种指针,当一个对象被创建后,它的每一个成员函数都会含有一个系统自动生成的隐含指针this。this指针指向被调用的成员函数所属的对象(谁调用成员函数,this指向谁),“this表示对象本身,非静态成员函数中才有this,静态成员函数没有”。
1.this并不是一个常规变量,而是一个右值,所以不能取得this的地址(即不能&this);
2.对非静态成员函数默认添加this指针,类型为classname *const this。
来源于网络
this指针使用
1.当形参类型与成员变量名相同时,用this指针来区分;
2.为实现对象的链式引用,在类的非静态成员函数中返回对象本身,可以用return *this,*this指向对象本身
- C++异常处理
一、异常情况
1.语法错误(编译错误):比如变量未定义,括号不匹配、关键字拼写错误等,编译 器在编译阶段能发现的错误,这种错误可以及 时被编译器发现,而且可以知道出错的位置和原因,方便改正。
2.运行时错误:比如说数组越界、系统内存不足、除数为0等。这类错误不易被发现,虽 然能通过编译进行运行,但运行时会出错,导致程序崩溃。为了有效处理运行时错误,C++引入异常机制来解决此问题。
二、处理办法
执行一个函数的过程中若发现异常,可以不用在本函数内立即进行处理,而是抛出该异常,让函数的调用者直接或间接处理这个问题。
其中异常处理机制由3个模块组成:try(检查)、throw(抛出)、catch(捕捉)。
- ++前置和后置的区别
i++(后置)的执行步骤:
1.先产生一个临时对象保存传入值;
2.传入值自增或自减;
3.返回临时对象;
++i(前置)的执行步骤:
1.传入值自增或自减;
2.返回对象引用;
- 内存分配(new/delete)
当调用new的时候,实际执行三步操作:
1.new表达式调用operator new (或者operator new[])的标准库函数, 该函数分配一块足够大、原始的、未命 名的内存空间以便存储特定类型的对象 (或对象的数组);
2.编译器运行相应的构造函数以构造这些对象,并为其传入初始值;
3.对象被分配了空间并构造完成,返回一个指向该对象的指针。
当调用delete表达式删除一个动态内存分配时,实际上执行了两步操作:
1.对所指对象或者所指的数组中的元素执行相应的析构函数;
2.编译器调用operator delete(或operator delete[])的标准库函数释放内存空间。
一条new表达式的执行过程总是先调用operator new函数以获取内存空间,然后在得到的内存空间中构造对象。与之相反,一条delete表达式的执行总是先销毁对象,然后调用operator delete函数释放对象所占的空间。
《C++ primer》
new和malloc的区别(下图来源于网络)
- sizeof运算符
一、作用
返回一条表达式或一个类型名字所占的字节 数,其满足右结合律,其所得的值是size_t类型 (size_t是一种机器相关的无符号类型)的常量表达式。
二、sizeof两种形式
sizeof (type) sizeof expr //sizeof返回值是表达式结果类型的大小
示例
sizeof data //data类型的大小
sizeof p; //指针所占空间大小
sizeof *p; //p所指类型的空间大小
三、sizeof运算符的结果部分依赖于其作用的类型
1.对char或者类型为char的表达式 执行sizeof运算,结果为1;
2.对引用类型执行sizeof运算得到被引用对象所占空间的大小;
3.对指针执行sizeof运算得到指针本身所占空间的大小;
4.对解引用指针执行sizeof运算得到指针指向对象所占空间的大小,指针不需有效;
5.对数组执行sizeof运算得到整个数组所占空间的大小,等价于对整个数组中所有的元素 各执行一次sizeof运算并将所得结果求和。
注意,sizeof运算不会把数组转换成指针来处理。注意:若要知道数组中元素个数,可以 通过sizeof(ia) / sizeof(*ia),其中ia为数组名。
6.对string对象或vector对象执行sizeof 运算只返回该类型固定部分的大小,不会计算对象中的元素各占多少空间。
- 运行时类型识别(RTTI)
运行时类型识别(run-time identification, RTTI)的功能主要由两个运算符实现:
1.typeid运算符,用于返回表达式类型;
2.dynamic_cast运算符,用于将基类的指针和或引用安全地转换成派生类的指针或引用
这两个运算符特别适用于以下情况:
1.使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数;
2.当操作被定义为虚函数时,编译器将根据对象的动态类型自动的选择正确的函数版本。
《c++ primer》
dynamic_cast详情请见《c++复习篇2》,接下来主要讲解typeid运算符。
typeid运算符:它允许程序向表达式提问:你的对象是什么类型。
一、形式
typeid(e)
其中e可以是任意表达式或类型的名字,其结果是一个常量对象的引用(对象类 型为type_info或者type_infode的公有派生类型)。
二、使用typeid运算符
通常情况下,可以使用typeid比较两个表 达式的类型是否相同,或比较一个表达式是否与指定类型相同:
Derived *dp = new Derived;
Base *bp = bp; //两个指针都指向Derived对象
//在运行时比较两个对象类型
if((typeid(*bp)) == (typeid(*dp))){
//bp和dp指向同一类型的对象
}
//检查运行时类型是否是某种指定的类型
if((typeid(*bp)) == (typeid(Derived))){
//bp实际指向Derived对象
}
注意:
1.typeid作用于指针时(非指针所指对象),返回的结果是该指针的静态编译时类型;
2.typeid是否需要运行时检查决定于表达式是否会被求值,只有当类型含有虚函数时,编译器才会对表达式求值。
type_info类
描述编译器在程序中生成的类型信息,此类的对象可以有效存储指向类型的名称和指针。