关键字与运算符
指针与引用
- 指针存放某个对象的地址,其本身就是变量(命了名的对象),本身就有地址,所以可以有指向指针的指针;可变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变
- 引用就是变量的别名,从一而终,不可变,必须初始化
- 不存在指向空值的引用,但是存在指向空值的指针
define 和 typedef 的区别
define:
- 只是简单的字符串替换,没有类型检查
- 是在编译的预处理阶段起作用
- 可以用来防止头文件重复引用
- 不分配内存,给出的是立即数,有多少次使用就进行多少次替换
typedef:
- 有对应的数据类型,是要进行判断的
- 是在编译、运行的时候起作用
- 在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝
define 和 inline 的区别
1、define:定义预编译时处理的宏,只是简单的字符串替换,无类型检查,不安全。
2、inline:先将内联函数编译完成的函数体直接插入被调用的地方,减少了压栈,跳转和返回的操作。没有普通函数调用时的额外开销
内联函数是一种特殊的函数,会进行类型检查
对编译器的一种请求,编译器有可能拒绝这种请求
C++中inline编译限制
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 内联函数声明必须在调用语句之前
override 和 overload
1、override是重写(覆盖)了一个方法
以实现不同的功能,一般是用于子类在继承父类时,重写父类方法
规则:
- 重写方法的参数列表,返回值,所抛出的异常与被重写方法一致
- 被重写的方法不能为private
- 静态方法不能被重写为非静态的方法
- 重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)
2、overload是重载,这些方法的名称相同而参数形式不同
一个方法有不同的版本,存在于一个类中。
规则:
- 不能通过访问权限、返回类型、抛出的异常进行重载
- 参数列表可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数列表必须不一样)
- 方法的异常类型和数目不会对重载造成影响
new 和 malloc
1、new内存分配失败时,会抛出bad_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL
2、使用new操作符申请内存分配时无须指定内存块的大小,而malloc则需要显式地指出所需内存的尺寸
3、opeartor new/operator delete可以被重载,而malloc/free并不允许重载
4、new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会
5、malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符
6、new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存
constexpr 和 const
const 表示“只读”的语义,constexpr 表示“常量”的语义
constexpr 只能定义编译期常量而 const 可以定义编译期常量也可以定义运行期常量。
你将一个成员函数标记为constexpr,则顺带也将它标记为了const。如果你将一个变量标记为constexpr,则同样它是const的。但相反并不成立,一个const的变量或函数,并不是constexpr的
constexpr变量
复杂系统中很难分辨一个初始值是不是常量表达式,可以将变量声明为constexpr类型,由编译器来验证变量的值是否是一个常量表达式。
必须使用常量初始化
constexpr int n = 20;
constexpr int m = n + 1;
static constexpr int MOD = 1000000007;
如果constexpr声明中定义了一个指针,constexpr仅对指针有效,和所指对象无关
constexpr int *p = nullptr; // 常量指针,顶层const
const int *q = nullptr; // 指向常量的指针,底层const
int* const q = nullptr; // const常量指针,顶层const
constexpr函数
constexpr函数是指能用于常量表达式的函数
函数的返回类型和所有形参类型都是字面值类型,函数体有且只有一条return语句
constexpr int new() { return 42; }
为了可以在编译过程展开,constexpr函数被隐式转换成了内联函数。
constexpr和内联函数可以在程序中多次定义,一般定义在头文件
constexpr 构造函数
构造函数不能说const,但字⾯值常量类的构造函数可以是constexpr
constexpr构造函数必须有一个空的函数体,即所有成员变量的初始化都放到初始化列表中。对象调用的成员函数必须使用 constexpr 修饰
const
指针常量: const int* ptr = new int(2);
不能改变对象的值
常量指针: int* const cptr = new int(2);
不能改变指向
左定值,右定向:指的是
const
在*
的左还是右边
顶层const:指针本身是常量
底层const:指针所指的对象是常量
若要修改const修饰的变量的值,需要加上关键字volatile
若想要修改const成员函数中某些与类状态无关的数据成员,可以使用mutable关键字来修饰这个数据成员
const和static成员
关键字 | 修饰常量 | 修饰成员变量 | 修饰成员函数 |
---|---|---|---|
const | 超出其作用域后空间会被释放,在定义时必须初始化,之后无法更改。 const形参可以接受const和非const类型的实参 |
只在某个对象的生命周期内是常量,而对整个对象而言是可变的不能赋值 不能在类外定义,只能通过构造函数初始化列表进行初始化【因为不同的对象对其const数据成员的值可以不同,所以不能在类中声明时初始化】 |
防止成员函数修改对象的内容『不能修改成员变量的值,但是可以访问』 const对象不可以调用非const的函数,但是非const的对象可以 |
static | 在函数执行后不会释放其存储空间 | 只能在类定义体内部声明,外部初始化,且不加static | 1.作为类作用域的全局函数『不能访问和调用非静态数据成员和调用非静态成员函数』 2.没有this指针『不能直接存取类的非静态成员,调用非静态成员函数』 3.不能声明为virtual |
const
和static
不能同时修饰成员函数,因为静态成员函数不含有this指针,即不能实例化,而const成员函数必须具体到某一实例
constexpr的好处
- 为一些不能修改数据提供保障,写成变量则就有被意外修改的风险。
- 有些场景,编译器可以在编译期对constexpr的代码进行优化,提高效率。
- 相比宏来说,没有额外的开销,但更安全可靠
volatile
与const绝对对立的,是类型修饰符。影响编译器编译的结果,用该关键字声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化;会从内存中重新装载内容,而不是直接从寄存器拷贝内容
作用:指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值,保证对特殊地址的稳定访问
使用场合:在中断服务程序和cpu相关寄存器的定义
for (volatile int i = 0; i < 10000; i++); // 会执行,不会被优化掉
extern
声明外部变量,在函数或者文件外部定义的全局变量
static
实现多个对象之间的数据共享+隐藏,并且使用静态成员还不会破坏隐藏原则;默认初始化为0
前置++与后置++
self& operator++() { // 前置
node = (linktype)((node).next);
return *this;
}
const self operator++(int) { // 后置
self tmp = *this;
++*this;
return tmp
}
为了区分前后置,重载函数是以参数类型来区分,在调用的时候,编译器默默给int指定为0
1、某文件中定义的静态全局变量(或称静态外部变量),其作用域是本文件。静态全局变量只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能使用它
2、如何判断一段程序是由C编译器还是由C++编译器编译的
#ifdef __cplusplus
cout << "C++";
#else
cout << "C";
#endif
3、C++函数中值的传递方式有:值传递、指针传递和引用传递
4、C和C++有什么不同?
- 从机制上,C是面向过程的,C++是面向对象的,提供了类。C也可以编写面向对象的程序,但是C++编写面向对象的程序比C容易
- 从使用场景:C适合要求代码体积小,效率高的场景,如嵌入式;C++适合更上层的复杂的场景;Linux的核心大部分是C写的,因为它是系统软件,效率要求极高
5、内联函数在编译时要做参数类型检查,这是内联函数跟宏相比的优势
6、内存的分配
(1) 静态存储区,程序编译时就已经分配好了,这块内存在程序的整个运行期间都存在。例如全局变量
(2) 在栈上创建,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元被自动释放
(3) 在堆上分配,也称动态内存分配,程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责用free和delete释放内存
7、全局变量和局部变量区别
- 生命周期不同:全局变量随主程序开始而创建,随主程序结束而销毁;局部变量在局部函数内部或者局部循环体内部存在,退出就不存在了
- 全局变量分配在全局数据段,局部变量则分配在堆栈里面