const修饰普通变量
const有以下几个方面的作用:
1)定义const常量,具有不可变性(只读变量)。
2)进行类型检查,使编译器对处理内容有更多了解,消除一些隐患。
3)避免意义模糊的数字出现,同样可以很方便地进行参数检查和修改。同宏定义一样,可以做到不变则已,一变都变。
4)保护被修饰的东西,防止被意外修改,增强了程序的健壮性。
5)为函数重载提供参考。
6)节省空间,避免不必要的内存分配。const定义的常量在程序运行过程中只有一份复制品,而#define定义的常量在内存中有若干个复制品。
7)提高了程序的运行效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,使得它成为一个编译期间的常量,没有了存储和读内存操作,使得它的运行效率也很高。
{
// 以常量初始化const修饰的只读变量 那么只读变量的值 事先存放在“符号常量表中” 不会立即给data开辟空间
const int data = 100; // 常量初始化
int* p = (int*)&data; // 对data取地址时,才会开辟空间
*p = 2000;
cout << " * p = " << *p << endl; // 2000
cout << "data = " << data << endl; // 100
}
{
// 以变量初始化const修饰的只读变量 那么只读变量会立即开辟空间(没有符号常量表)
int data = 100;
const int data2 = data; // 变量初始化
int* p = (int*)&data2;
*p = 200;
cout << "data2 = " << data2 << endl; // 200
cout << "*p = " << *p << endl; // 200
}
register修饰寄存器变量
(能够提高变量访问效率,提升程序运行速度)
如果变量高频繁使用,会自动将变量存储在寄存器中 目的:提高访问效率
使用register关键字修饰,能够将变量直接放入寄存器中
{
// 注意尽量不要对寄存器变量取地址,因为取地址是对内存的操作
register int data = 10;//data放入寄存器中
}
了解一下:register 修饰的变量,只是尽量放入寄存器中
volatile关键字
强制访问内存
防止编译器优化
{
volatile int data = 10;//对data的访问,必须从内存访问
}
大部分对单片机操作,传感器操作
//sizeof测量类型的大小
{
cout << "sizeof(int) = " << sizeof(int) << endl; //4
cout << "sizeof(char) = " << sizeof(char) << endl; //1
cout << "sizeof(short) = " << sizeof(short) << endl; //2
cout << "sizeof(long) = " << sizeof(long) << endl; //4
cout << "sizeof(float) = " << sizeof(float) << endl; //4 32位平台
cout << "sizeof(double) = " << sizeof(double) << endl; //8
}
typedef 关键字
功能:给已有的类型重新取个变量名
不能创建新的类型。
将长且复杂的类型名 去一个短小的名称。
typedef作用的步骤:
1)先用已有的类型 定义一个普通的变量
2)用别名 替换 变量名
3)在整个表达式最前面 加typedef
{
typedef int INT32; // INT32就是int类型的别名
INT32 data;
int num; // 已有类型仍然有效
}
案例1:给int arr[5]取个别名
{
typedef int myarr[5];
myarr arr = { 1,2,3,4,5 };
for (int i = 0; i < sizeof(myarr) / sizeof(int); i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
案例2:给int *取个别名
{
typedef int* myp;
myp p; //p的类型就是int *类型
}
随机数 函数:rand()
//需要头文件
#include "time.h"
{
// 设置随机数种子 time(NULL)获取当前时间
srand(time(NULL));
// 产生随机数据的函数rand()
cout << rand() << endl;
}
编译一个c文件需要四个阶段:
预处理 -> 编译 -> 汇编 -> 链接
友元函数的关键字
friend
借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。
在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加 friend 关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。
友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。
#include <iostream>
using namespace std;
class Student{
public:
Student(char *name, int age, float score);
public:
friend void show(Student *pstu); //将show()声明为友元函数
private:
char *m_name;
int m_age;
float m_score;
};
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
//非成员函数
void show(Student *pstu)
{
cout<<pstu->m_name<<"的年龄是 "<<pstu->m_age<<",成绩是 "<<pstu->m_score<<endl;
}
int main(){
Student stu("小明", 15, 90.6);
show(&stu); //调用友元函数
Student *pstu = new Student("李磊", 16, 80.5);
show(pstu); //调用友元函数
return 0;
}
mutable关键字
mutable 英文中表示,易变的,不定的;性情不定的,而在代码中表示 可变数据成员。
由前面整理的 const详解 知道,由const修饰的成员函数中,不能对成员变量进行修改,因为隐藏的形参是 const 属性的。
而实际开发过程中,我们可能需要对某些成员变量进行修改,就需要用到 mutable。
class constA
{
public:
constA(int a):m_a(a){}
void funcA() const { cout << ++m_a << endl; }
mutable int m_a;
};
int main(int argc, char *argv[])
{
constA ab(10);
ab.funcA();
const constA abc(11);
abc.funcA();
abc.m_a = 15;
abc.funcA();
}
输出
11
12
16
这里可以看到在const的成员函数中,可以对mutable修饰的变量进行修改。 而对于const对象,它的成员变量依然可以进行修改。
总结一句话:一个 可变数据成员,永远不会是const,即使它是const 对象的成员。
函数重载 关键字 operator
(函数重载是一种静态多态)
简单理解:几个函数的名字相同,但其参数不同或者是参数类型不同;大体相同,细节不同。
举例:交换两个数的值,其中包括int、float、char、double等类型。在C语言中必须要用不用的函数名加以区分,这样的代码会带来很多不便,于是在C++中提出了用一个函数名定义多个函数,也就是函数重载。
函数重载的规则:
函数名必须相同、参数列表必须不同(体现在个数、类型、排列顺序等不同上)、函数的返回类型可以相同也可以不相同、仅仅返回类型不同不足以成为函数的重载。
函数重载的作用:重载函数通常用在同一个作用域内,用同一个函数名命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
#include<iostream>
using namespace std;
int Add(int a, int b){
return a + b;
}
double Add(double a, double b){
return a + b;
}
float Add(float a, float b){
return a + b;
}
int main(){
cout << Add(1,2) << endl;
cout << Add(3.5, 4.5) <<endl;
cout << Add(2.22, 3.33) << endl;
return 0;
}
运算符重载
C++中,运算符能够被用户定义,就是说C++能够为运算符提供数据类型的特殊含义,这种能力称为运算符的重载。
作用举例:
(1)在String类的类中重载运算符"+",以便用"+"就可以连接两个字符串.
(2)重载运算符"+",使其作用为点坐标的相加。
代码举例(复数相加):
#include<iostream>
using namespace std;
class Complex {
private:
int real, imag;
public:
Complex(int r = 0, int i =0) {real = r; imag = i;}
Complex operator + (Complex const &obj) { //重载操作
Complex res;
res.real = this->real + obj.real;
res.imag = this->imag + obj.imag;
return res;
}
void print() { cout << real << " + i" << imag << endl; }
};
int main()
{
Complex c1(10, 5), c2(2, 4); //对数据以类的方式处理
Complex c3 = c1 + c2;
c3.print();
}
需要注意的是:为了使运算符重载起作用,至少一个操作数必须是用户定义的类对象。
C++共用体union
- 结构体:所有成员拥有独立空间
- 共用体:所有成员共享一块空间
共用体关键字:union
共用体的空间 是由最大的成员决定
union Data
{
char a;
short b;
int c;
};
注意:对任何一成员的操作也是对同一空间的操作;
虽然都是对同一空间进行操作,但是操作的大小是由成员自身的大小决定
C++动态空间分配 new & delete
动态空间分配的关键字是new,删除空间是delete
动态分配
1.在程序运行的过程中,根据大小自由分配空间
2.按需分配
3.分配在堆区,一般使用特定的关键字进行分配
new和delete
new申请堆区空间 delete释放空间;
int *p = NULL;
p = new int; // 从堆区申请int类型大小的空间
*p = 100;
cout<<"*p = "<<*p<<endl;//100
//释放空间
delete p;
//new申请空间的同时 初始化空间内容
int *p1 = NULL;
p1 = new int(10);
cout<<"*p1 = "<<*p1<<endl;//10
delete p;
new和delete操作数组空间
new与[]结合表示申请数组空间 delete释放的时候也需要加[];
int *arr = NULL;
arr = new int[5]{10,20,30,40,50}; // 从堆区申请int类型大小的空间
int i = 0;
for(; i<5; i++)
{
//cout<<arr[i]<<" ";
cout<<*(arr+i)<<" ";
}
cout<<endl;//1
//如果new有[] 那么delete是也需要[]
delete [] arr;
virtual 关键字
虚函数的定义:虚函数,是指被virtual关键字修饰的 成员函数。
虚成员函数应在类内定义声明,且必须有定义(实现)。注意,在类外定义实现虚成员函数时,不能再加 virtual 关键字。
子类重写 父类的虚函数:函数名、返回值类型、参数类型个数顺序 必须完全一致
多态的条件:有继承、子类重写父类的虚函数,父类指针指向子类空间
如果一个类中的成员函数被virtual修饰,那么这个函数就是虚函数。类就会产生一个虚函数指针(vfptr)指向了一张虚函数表(vftable)。如果这个类没有涉及继承,这时虚函数中记录就是当前虚函数的入口地址。
class Animal
{
public:
// 虚函数
virtual void speak(void)
{
cout << "动物在说话" << endl;
}
};
class Animal
{
public:
// 纯虚函数
virtual void speak(void) = 0;
};
一旦类中有纯虚函数,那么这个类 就是抽象类。
抽象类 不能实例化 对象。 (Animal ob; 错误)
抽象类 必须被继承 同时 子类必须重写 父类的所有纯虚函数,否则 子类也是抽象类。
抽象类的主要目的 是设计 类的接口
virtual修饰析构函数
虚析构:通过父类指针 释放整个子类空间。
应用场景:一个函数的参数是基类指针,在主调函数 new 分配了内存,需要在被调函数中使用完该对象后 delete
class Animal
{
public:
// 虚函数
virtual void speak(void)
{
cout << "动物在说话" << endl;
}
// 虚析构
virtual ~Animal()
{
cout << "Animal的析构函数" << endl;
}
};
class Animal
{
public:
// 纯虚函数
virtual void speak(void)
{
cout << "动物在说话" << endl;
}
// 纯虚析构函数 必须在类中声明,在类外实现
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout << "Animal的析构函数" << endl;
}
虚析构和纯虚析构的区别?
虚析构:virtual修饰,有函数体,不会导致父类为抽象类。
纯虚析构:virtual修饰,=0,函数提必须在类外实现,导致父类为抽象类。