const 关键字
const的作⽤:
被它修饰的值不能改变,是只读变量。必须在定义的时候就给它赋初值。
1、常量指针(底层const )
常量指针:
是指定义了⼀个指针,这个指针指向⼀个只读的对象,不能通过常量指针来改变这个对象的值。常量指针强调的是
指针对其所指对象的不可改变性。
特点:
靠近变量名。
形式:
(1)const 数据类型 *指针变量 = 变量名
(2)数据类型 const *指针变量 = 变量名
示例:
int temp = 10;
const int* a = &temp;
int const *a = &temp;
// 更改:
*a = 9; // 错误:只读对象
temp = 9; // 正确
2、指针常量(顶层const)
指针常量:
指针常量是指定义了⼀个指针,这个指针的值只能在定义时初始化,其他地⽅不能改变。指针常量强调的是指针的
不可改变性。
特点:
靠近变量类型。
形式:
数据类型 * const 指针变量=变量名
示例:
int temp = 10;
int temp1 = 12;
int* const p = &temp;
// 更改:
p = &temp2; // 错误
*p = 9; // 正确
static关键字的作⽤
static
关键字主要⽤于控制变量和函数的⽣命周期、作⽤域以及访问权限。
1. 静态变量
- 在函数内部使⽤
static
关键字修饰的变量称为静态变量。 - 静态变量在程序的整个⽣命周期内存在,不会因为离开作⽤域⽽被销毁。
- 静态变ᰁ默认初始化为零(对于基本数据类型)。
void exampleFunction() {
static int count = 0; // 静态变量
count++;
cout << "Count: " << count << endl;
}
- 静态函数
- 在类内部使⽤
static
关键字修饰的函数是静态函数。 - 静态函数属于类⽽不是类的实例,可以通过类名直接调⽤,⽽⽆需创建对象。
- 静态函数不能直接访问⾮静态成员变量或⾮静态成员函数。
class ExampleClass {
public:
static void staticFunction() {
cout << "Static function" << endl;
}
};
- 静态成员变量
- 在类中使⽤
static
关键字修饰的成员变量是静态成员变量。 - 所有类的对象共享同⼀个静态成员变量的副本。
- 静态成员变量必须在类外部单独定义,以便为其分配存储空间。
class ExampleClass {
public:
static int staticVar; // 静态成员变量声明
};
// 静态成员变ᰁ定义
int ExampleClass::staticVar = 0;
- 静态成员函数
- 在类中使⽤
static
关键字修饰的成员函数是静态成员函数。 - 静态成员函数不能直接访问⾮静态成员变ᰁ或⾮静态成员函数。
- 静态成员函数可以通过类名调⽤,⽽不需要创建类的实例。
class ExampleClass {
public:
static void staticMethod() {
cout << "Static method" << endl;
}
};
- 静态局部变量
- 在函数内部使⽤
static
关键字修饰的局部变量是静态局部变量。 - 静态局部变量的⽣命周期延⻓到整个程序的执⾏过程,但只在声明它的函数内可⻅。
void exampleFunction() {
static int localVar = 0; // 静态局部变量
localVar++;
cout << "LocalVar: " << localVar << endl;
}
const关键字的作用
const
关键字主要⽤于指定变量、指针、引⽤、成员函数等的性质
- 常量变量:声明常量,使变量的值不能被修改。
- 指针和引用:声明指向常量的指针,表示指针所指向的值是常量,不能通过指针修改。声明常量引⽤,表示引⽤的值是常量,不能通过引⽤修改。
const int* ptr = &constantValue; // 指向常量的指针
const int& ref = constantValue; // 常量引⽤
- 成员函数:⽤于声明常量成员函数,表示该函数不会修改对象的成员变量(对于成员变量是⾮静态的情况)。
- 常量对象:声明对象为常量,使得对象的成员变量不能被修改。
- 常引⽤参数:声明函数参数为常量引⽤,表示函数不会修改传⼊的参数。
- 常量指针参数:声明函数参数为指向常量的指针,表示函数不会通过指针修改传⼊的数据。
define 和 typedef 的区别
define
- 只是简单的字符串替换,没有类型检查
- 是在编译的预处理阶段起作⽤
- 可以⽤来防⽌头⽂件重复引⽤
- 不分配内存,给出的是⽴即数,有多少次使⽤就进⾏多少次替换
typedef
- 有对应的数据类型,是要进⾏判断的
- 是在编译、运⾏的时候起作⽤
- 在静态存储区中分配空间,在程序运⾏过程中内存中只有⼀个拷⻉
define 和 inline 的区别
1、define:
定义预编译时处理的宏,只是简单的字符串替换,⽆类型检查,不安全。
2、inline:
inline
是先将内联函数编译完成⽣成了函数体直接插⼊被调⽤的地⽅,减少了压栈,跳转和返回的操作。没有普通
函数调⽤时的额外开销;
内联函数是⼀种特殊的函数,会进⾏类型检查;
对编译器的⼀种请求,编译器有可能拒绝这种请求;
C++中inline编译限制:
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞⼤
- 内联函数声明必须在调⽤语句之前
const和define的区别
const
⽤于定义常量;⽽define
⽤于定义宏,⽽宏也可以⽤于定义常量。都⽤于常量定义时,它们的区别有:
- const⽣效于编译的阶段;define⽣效于预处理阶段。
- const定义的常量,在C语⾔中是存储在内存中、需要额外的内存空间的;define定义的常量,运⾏时是直接
的操作数,并不会存放在内存中。 - const定义的常量是带类型的;define定义的常量不带类型。因此define定义的常量不利于类型检查。
new 和 malloc的区别
1、new
内存分配失败时,会抛出bac_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
constexpr函数:
constexpr函数是指能⽤于常ᰁ表达式的函数。
函数的返回类型和所有形参类型都是字⾯值类型,函数体有且只有⼀条return语句。
constexpr int new() {
return 42;}
为了可以在编译过程展开,constexpr函数被隐式转换成了内联函数。
constexpr和内联函数可以在程序中多次定义,⼀般定义在头⽂件。
constexpr 构造函数:
构造函数不能说const,但字⾯值常ᰁ类的构造函数可以是constexpr。
constexpr构造函数必须有⼀个空的函数体,即所有成员变ᰁ的初始化都放到初始化列表中。对象调⽤的成员函数
必须使⽤ constexpr 修饰
const:
指针常量: const int* d = new int(2);
常量指针: int *const e = new int(2);
区别⽅法:
左定值,右定向:指的是const在*的左还是右边
拓展:
顶层const:指针本身是常量;
底层const:指针所指的对象是常量;
若要修改const修饰的变量的值,需要加上关键字volatile
;
若想要修改const成员函数中某些与类状态⽆关的数据成员,可以使⽤mutable关键字来修饰这个数据成员;
『const和static的区别』
constexpr的好处
- 为⼀些不能修改数据提供保障,写成变ᰁ则就有被意外修改的⻛险。
- 有些场景,编译器可以在编译期对constexpr的代码进⾏优化,提⾼效率。
- 相⽐宏来说,没有额外的开销,但更安全可靠。
volatile
定义:
与const绝对对⽴的,是类型修饰符]影响编译器编译的结果,⽤该关键字声明的变量表示该变量随时可能发⽣变化,与该变量有关的运算,不要进⾏编译优化;会从内存中量新装载内容,⽽不是直接从寄存器拷⻉内容。
作⽤:
指令关键字,确保本条指令不会因编译器的优化⽽省略,且要求每次直接读值,保证对特殊地址的稳定访问
使⽤场合:
在中断服务程序和cpu相关寄存器的定义
举例说明:
空循环:
for(volatile int i=0; i<100000; i++); // 它会执⾏,不会被优化掉
extern
定义:声明外部变量【在函数或者⽂件外部定义的全局变量】
static
作⽤:实现多个对象之间的数据共享 + 隐藏,并且使⽤静态成员还不会破坏隐藏原则;默认初始化为0
前置++与后置++
为了区分前后置,重载函数是以参数类型来区分,在调⽤的时候,编译器默默给int指定为⼀个0
1、为什么后置返回对象,⽽不是引⽤
因为后置为了返回旧值创建了⼀个临时对象,在函数结束的时候这个对象就会被销毁,如果返回引⽤,那么我请问
你?你的对象对象都被销毁了,你引⽤啥呢?
2、为什么后置前⾯也要加const
其实也可以不加,但是为了防⽌你使⽤i++++,连续两次的调⽤后置++᯿载符,为什么呢?
原因:
它与内置类型⾏为不⼀致;你⽆法活得你所期望的结果,因为第⼀次返回的是旧值,⽽不是原对象,你调⽤两次后
置++,结果只累加了⼀次,所以我们必须⼿动禁⽌其合法化,就要在前⾯加上const。
3、处理⽤户的⾃定义类型
最好使⽤前置++,因为他不会创建临时对象,进⽽不会带来构造和析构⽽造成的格外开销。