C++基础知识
- 单行注释 //
多行注释 /**/ - 常量
#define 常量名 常量值
const 数据类型 常量名 = 常量值
- 关键字不要和变量或常量重名
- 数据类型
- 短整型(2字节)-整型(4字节)-长整型(4字节)-长长整型(8字节)
short<int<=long<=long long
实型 - float(4)-double(8)
- 科学计数法
3e2 3*10^2
3e-2 3*0.1^2
- sizeof
sizeof(datatype\变量)
- 字符型
char ch = 'a'
字符串
C风格char str[] = "abc"
C++风格string str = "abc"
- 布尔型
bool flag = true
- 键入
int a = 0;
cin >> a;
float b = 3.14f;
cin >> b;
char ch = 'a';
cin >> ch;
string str = "hello";
cin >> str;
bool flag = true;
cin >> flag;
- 三目运算符
表达式1 ?表达式2 : 表达式3
返回的是变量,可以继续进行赋值操作 - goto 无条件跳转语句
- 二维数组定义时可以省去行数
- 查看二维数组的首地址
(int)arr
二维数组的第一行首地址(int)arr[0]
二维数组第一个元素的首地址(int)&arr[0][0]
int 不是必须加的。只是转换类型 - 值传递 形参改变不会改变实参
地址传递 传递的是地址,实参可以改变 - 函数的声明
无需在main前定义函数int max(int a, int b);
声明可以多次,定义只有一次 - 函数的分文件编写
头文件声明函数
源文件导入头文件 应用函数 - 指针 保存地址 初始指针
int * p
& 取地址 取出变量a的地址
* 解引用 找到对应地址中的内容
32位操作系统 x86 所有指针类型均占用4个字节
64位操作系统 x64 所有指针类型均占用8个字节 - 空指针 用于给指针变量进行初始化 不可以访问0~255是系统占用的,因此不可以访问
- 野指针 指向非法的内存空间 直接给指针一个未知地址
- const 修饰指针
a. 常量指针
指针的指向可以更改,但是指针指向的值不可以改
int a = 10;
const int * p = &a;
b. 指针常量
指针的指向不可以更改,指针指向的值可以改
int a = 10;
int * const p = &a;
c. 同时修饰
均不可以修改
int a = 10;
const int * const p = &a;
记忆技巧 const和*的位置关系,修饰谁谁不可以修改 - 结构体 一些类型数据的集合的一个类型
struct 结构体名 {结构体成员列表};
struct Student{
string name;
int age;
int score;
}
struct 创建变量可以省略,定义不可以省略
三种创建变量方式
1、struct Student s1;
s1.name = "zhangsan";
s1.age = 18;
s1.socre = 100;
2、struct Student s2 = {"lisi", 19, 80};
3、struct Student{
string name;
int age;
int score;
}s3; 很少用
- 结构体数组
将自定义的结构体放到数组中方便维护
定义
struct 结构体名 {结构体成员列表};
struct Student{
string name;
int age;
int score;
};
创建结构体数组
struct Student stuarr[3] = {
{"12",18,50},
{"22",18,50},
{"32",18,50},
};
22、结构体指针
定义
struct 结构体名 {结构体成员列表};
struct Student{
string name;
int age;
int score;
};
struct Student stu1 = {"12", 18, 50};
& 取什么类型数据的地址,返回的就是什么类型
struct Student *p = &s;
通过结构体指针访问结构体中的属性,需要利用->
name = p->name;
23、结构体嵌套结构体
结构体中成员可以是另一个结构体
struct Student{
string name;
int age;
int score;
};
struct Teacher{
int id;
string name;
int score;
struct Student stu;
};
teacher T1;
T1.id = 1
T1.name = "hh"
T1.stu.name = "qq"
T1.stu.age = 20
T1.stu.score = 18
- 结构体做函数参数
- 值传递 .访问 形参改变,实参不变
- 地址传递 ->访问 形参改变,实参改变
- 结构体中const使用场景
结构体地址传递,节省内存空间
形参之前加入const,防止误修改 - 内存的分区模型
代码区 程序运行前
存放函数体的二进制代码,存放CPU执行的机器指令,共享,只读
全局区 程序运行前
全局变量 静态变量 常量,程序结束后由操作系统释放
const修饰局部变量变为局部常量也不在全局区
栈区 程序运行后
编译器自动分配释放 存放函数的参数值、局部变量
不要返回局部变量的地址
堆区 程序运行后
程序员分配释放 ,若程序员不释放,程序结束由操作系统回收
new int(10)
创建堆区数据,返回的是这个数据的地址即相应的指针
delete p
释放堆区数据,再访问会报错
new * arr = new int[10]
创建数组堆区
delete[] arr
释放数组
26. 引用 给变量起别名
int a = 10
int &b = a
注意事项
-引用必须要初始化
-引用一旦初始化后,就不可以更改了
-赋值操作不是更改引用
引用做函数参数
void Swap(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
int a = 10;
int b = 20;
Swap(a, b);
\\ 上面操作相当于int &a = a;给a起了个别名,函数中同样可以修改实参
引用做函数的返回值
-不要返回局部变量的引用 ,局部变量保存在栈区,函数结束会释放
-如果函数的返回值是引用,这个函数调用可以作为左值
引用的实质
本质:引用的本质在C++内部实现是一个指针常量
常量引用
作用:常量引用主要用来修饰形参,防止误操作
错误
int a = 10;
int &ref = 10;
正确
const int & ref = 10;
编译器替换为
int temp = 10;
const int & ref = temp;
主要应用
防止形参改变影响原始值
将形参变为 const int &val
- 函数提高
函数的默认参数
-如果函数从某个参数后为默认参数,之后的参数都必须为默认参数,和python一样
-如果函数声明有默认参数。函数实现就不能有默认参数,函数和声明中只能有一个默认参数
函数的占位参数
目前占位参数无用,之后会用到
占位参数还可以为默认参数
void func (int a, int){
cout << a <<endl;
}
func(10, 10);
void func (int a, int = 10){
cout << a <<endl;
}
func(10);
函数重载
函数名可以相同,提高复用性
满足条件:
-同一个作用域下
-函数名称相同
-函数参数类型不同,或者个数不同或者顺序不同
注意事项
-函数的返回值不可以作为函数重载的条件
-引用作为重载的条件
是否加const可以作为重载条件
-函数重载碰到默认参数,会出现二义性,避免出现这种情况
如果只传一个参数,编译器不知道调用哪个,如果传入两个则没有这种问题
- 类和对象
class 类名 {属性 行为}
访问权限
公共权限public 成员 类内可以访问,类外可以访问
保护权限protected 成员 类内可以访问,类外不可以访问 儿子可以访问父亲的保护内容
私有权限private 成员 类内可以访问,类外不可以访问 儿子不可以访问父亲的私有内容
29. struct和class的区别
struct默认权限是公共权限
class默认权限是私有权限
30. 成员属性设置为私有
-可以自己控制读写权限
-对于写可以检测数据的有效性
31. 对象的初始化和清理
构造函数和细够函数,自动由编译器调用,如果自己不写,编译器会提供,不过是空实现
构造函数的分类和调用
按照参数分类 无参构造(默认构造)和有参构造
按照类型分类 普通构造 拷贝构造 Preson(const Person &p)
(将传入的人身上所有的属性,拷贝到我身上)
调用
括号法
显示法
隐式转换法
拷贝构造函数的调用时机
-使用一个已经创建完毕的对象来初始化一个对象
-值传递的方式给函数参数传值
-以值方式返回局部对象
构造函数的调用规则
深拷贝和浅拷贝
浅拷贝是简单的赋值复制
析构函数可以定义堆区数据释放,而浅拷贝带来的问题就是堆区内存重复释放
深拷贝,自己申请堆区存储数据
初始化列表
类对象作为类成员
当其他类对象作为本类成员时,先构造其它类,在构造本类
析构时正好相反,先析构本类,再析构其它类
静态成员
-静态成员变量 不属于某一个对象上,所有对象都共享一份数据
通过对象来访问 通过类名进行访问
也是拥有权限的,设置为私有则类外无法访问
a所有对象共享同一份数据,b编译阶段就分配内存,c类内声明,类外初始化操作
-静态成员函数
a所有对象共享同一个函数 b静态成员函数只能访问静态成员变量(因为无法区分普通变量属于哪个创建对象,因为这个函数是共享的)
也有访问权限
两种访问方式
- 对象模型和this指针
空对象占用内存空间为1,为了区分空对象占内存的位置,每个空对象应该有一个独一无二的内存地址
成员变量和成员函数是分开存储的
计算对象占用内存大小时
非静态成员变量属于类的对象上,静态成员变量不属于类对象上,非静态成员函数也不属于类对象上,静态成员函数也不属于类对象上
this指针
this指针指向 被调用的成员函数 所属的对象
a 解决名称冲突 类内成员变量和构造函数的形参变量名称相同
b 返回对象本身 用*this (类似于python中的self??)
返回的值是一个对象则是新创建的一个对象,加上引用才是返回一个地址
空指针调用成员函数
const修饰成员函数 常函数
常函数内不可以修改成员属性
this指针的本质 是指针常量 指针的指向不可以修改
mutable 加入后可以修改
常对象 const修饰对象
常对象里面属性也不可以修改,除了加mutable修饰
常对象只能调用常函数
33. 友元
让一些特殊的类或函数可以访问私有属性
-全局函数做友元
在类中声明一下 friend 函数声明
-类做友元
在类中声明一下 friend 类
-成员函数做友元
在类中声明一下 friend 类::成员函数
34. 运算符重载
运算符重载也可以发生函数重载
-加号运算符重载
全局函数重载
成员函数重载
-左移运算符重载
-递增运算符重载
-赋值运算符重载
C++编译器至少给一个类添加4个函数
默认构造函数、默认析构函数、默认拷贝构造函数、operator+函数
-关系运算符重载
-函数调用运算符重载()
由于使用起来非常类似于函数调用,因此称为仿函数
类+()是匿名对象,这一行结束直接释放
35. 继承,减少重复代码
继承方式
公共继承, 保护继承,私有继承
父类中所有非静态成员属性都会被子类继承下去,私有属性被继承了,但是被编译器隐藏了
继承中的构造和析构顺序
先构造父类,后子类,析构相反
继承中同名成员处理
当子类中有和父类同名的成员函数时,会隐藏所有父类中的同名函数,访问需要添加作用域
直接调用是调用子类的
如果通过子类对象,访问父类的中的同名成员,需要加作用域
继承中同名静态成员处理方式
-通过对象访问
-通过类名访问
多继承语法
class 子类:父类1,父类2
菱形继承
利用虚继承解决菱形继承问题
-
多态
-静态多态
地址早绑定
-动态多态
地址晚绑定
父类中函数变为虚函数
满足条件 1、继承条件2、子类重写父类的虚函数
使用 父类的指针或引用指向子类对象
好处 1、组织结构清晰2、可读性强3、对于前期和后期扩展以及维护性高 -
纯虚函数和抽象类
纯虚函数virtual void func() = 0
包含纯虚函数的类为抽象类,抽象类无法实例化,子类必须重写纯虚函数 -
虚析构和纯虚析构
利用虚析构解决父类指针释放子类对象时不干净的问题,如果子类中没有堆区数据,可以不写虚析构或纯虚析构
纯虚析构有声明也要有实现,有了纯虚析构之后,也为抽象类 -
文件操作
-文本文件
写文件
1、#include2、创建对象流 ofstream ofs; 3、打开文件ofs.open(""文件路径“,打开方式) 4、写数据 ofs<<"写入的数据" 5、关闭文件 ofs.close();
读文件
1、#include2、创建对象流 ifstream ifs; 3、打开文件ifs.open(""文件路径“,打开方式) 4、读数据 四种方式 5、关闭文件 ifs.close();
-二进制文件 -
模板
模板只是一个框架,不能单独使用,并不是万能的
函数模板
template<typename T>
函数声明或定义
mySwap(T &a, T &b){
T temp = a
a=b;
b=temp;
}
int a = 10;
int b = 20;
两种使用方式
1、自动类型推导
mySwap(a,b);
2、显示指定类型
mySwap<int>(a, b);
注意事项:
自动类型推导,必须推导出一致的数据类型T,才可以使用
模板必须要确定出T的数据类型才可以使用
普通函数和函数模板的区别
-普通函数调用可以发生隐式类型转换
-函数模板 自动推导不会发生
-函数模板 显示指定类型,可以发生
普通函数和函数模板调用规则
-优先调用普通函数
-可以通过空模板参数列表 强制调用 函数模板
-函数模板可以发生函数重载
-如果函数可以产生更好的匹配,优先调用函数模板
模板的局限性
类模板和函数模板之间的区别
-只有显示指定类型的使用方式
-类模板参数列表可以有默认参数
类模板中成员函数只有在调用时才会创建出来
通过类模板创建的对象,有三种方式向函数中 进行传参
-指定传入类型
-参数模板话
-整个类模板化
类模板与继承
指定父类模板中的参数列表
类模板分文件编写
-直接包含文件本身
-.h和.cpp写在一起,.hpp文件后缀
类模板与友元函数
-全局函数类内实现
正常实现
-全局函数类外实现
需要让编译器提前知道全局函数的存在
- STL
面向对象三个特性
封装 继承 多态