const
常量指针const int *p = &a; 指针的指向可以修改,但指针指向的值不能修改。
指针常量int * const p =&a;指针的指向不可以改,指针指向的值可以改。
const即修饰指针又修饰常量 const int * const p =&a; 指针的指向和指针指向的值都不可以修改。
* 与 ->
struct Student{
string name;
int age;
int score;
} liming;
student *p = &liming;
liming.name == p->name; //√
void printStudent(student *p){ //地址传递 指针接收
cout << p->name << endl;
}
int main(){
printStudent(&liming);
}
四个区
运行前
- 代码区: 存放机器指令,只读,共享
- 全局区: 全局变量,静态变量(static,全局/局部),字符串常量(全局/局部)。操作系统管理其生命周期 运行时
- 栈区: 不要返回局部变量的地址
- 堆区:
int* p = new int(10);
delete p;
int* arr = new int[10];
delete[] arr;
引用
- 给变量起别名
int a = 10;
int &b = a;
注:必须初始化,初始化后不可改变。
- 引用做参数
//swap 引用达到交换的目的
void swap(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
int a = 10;
int b = 20;
swap(a,b);
- 引用做函数返回值
不要返回局部变量的引用;函数的调用可以作为左值
int& test(){
static int a = 10; //static 变量变为全局
return a;
}
int main(){
int &ref = test();
cout<<ref<<endl; //10
test() = 1000;
cout<<ref<<endl; //1000
}
本质是指针常量。
int a = 1;
int &ref = a;
int * const ref = &a; //相当于这个
const int & temp =10; //合法 不允许修改 相当于,int temp = 10; const int & ref =temp;
函数
默认参数
- 默认参数 放最后,从默认参数开始到右都得有
int sum(int a,int b = 10){
return a+b;
}
sum(10); //结果20
- 声明和实现 只能有一个有默认参数,避免二义性 函数重载
返回值不同,不可以作为函数重载的条件。
void func(const int &a){ //1
}
void func(int &a){ //2
}
int a = 10;
func(a); //使用2函数
func(10); //使用1函数
对象
封装 继承 多态
struct 与 class
struct C1{
int m_1;
}
class C2{
int m_2;
}
int main(){
C1 c1;
C2 c2;
c1.m_1 = 1; // √ struct内默认是公共的
c2.m_2 = 1; // × class内默认是公共的
}
构造函数
- Person p2(); //×
- 匿名对象 构造后会执行析构
- 不用拷贝构造函数生成匿名对象,若有p3,则在进行Person(p3)等价于Person p3,重复定义
- Person p4 = 10;相当于Person p4 = Person(10); 普通构造
- Person p5 = p4;相当于Person p5 = Person(p4); 拷贝构造 初始化列表
class Person{
public:
Person(int a,int b,int c) :m_A(a),m_B(b),m_C(c){
}
int m_A;
int m_B;
int m_C;
}
静态成员变量
- 共享同一份
- 编译阶段分配内存
- 类内声明 类外初始化 静态成员函数
- 共享1个函数
- 只能访问静态成员变量 const修饰成员函数
- 常函数 成员函数后加const,常函数内不可以修改成员属性,若想修改成员属性加mutable
- 常对象 只能调用常函数
友元
friend
- 全局函数做友元
- 类做友元
- 成员函数做友元 运算符重载
Person operator+ (Person &p1,Person &p2){
... ...
} //重载+
Person p3 = p1 + p2;
继承
- 公共继承 保护继承 私有继承
- 开发人员命令提示工具查看对象模型 cl /d1 reportSingleClassLayout类名 文件名
- 需继承解决菱形继承的问题 多态
- 静态多态 函数重载,运算符重载--->早绑定
- 动态多态 派生类+虚函数实现 ---->晚绑定,父类的指针或者引用指向子类的对象
- 纯虚函数 父类虚函数无实际意义,使用纯虚函数。
virtual void speak() = 0;
- 抽象类 有纯虚函数的就是,无法实例化对象。子类必须重写纯虚函数,否则无法实例化 析构与纯析构
- 多态使用时候,若子类中有属性开辟到堆区,父类指针在释放时,无法调用子类的析构代码,将父类的析构函数改为虚析构或者纯虚析构
virtual ~Animal(){ //虚析构
cout<<"Animal析构函数调用"<<endl;
}
virtual ~Animal() = 0; //纯虚析构,也属于抽象类,无法实例化对象 类内声明 类外实现 与虚析构不共存
文件
<fstream>
两种
- 文本文件
- 二进制文件 三操作
- ofstream
- ifstream
- fstream
模板
函数模板
template<typename T>
template 声明创建模板; typename 表明其后的符号是一种数据类型,也可以用class代替; T 通用的数据类型。
template<typename T>
void mySwap(T &a,T &b){
T temp =a;
a=b;
b=temp;
}
int a = 10;
int b= 20;
mySwap(a,b); //1.自动类型推导,必须推导出一致的数据类型才可以使用
mySwap<int>(a,b); //2.显示指定类型
模板必须确定T的数据类型才可以使用
普通函数 vs 函数模板:
- 隐式类型转换: 1.普通函数可以隐式转换 2.函数模板中的显示指定类型可以发生隐式子类型类型转换 3.函数模板中的自动类型推导不会发生隐式类型转换
- 调用规则: 1.优先调用普通函数 2.通过空模板参数列表,强制调用
myprint<>(a,b);3.函数模板也可以重载 4.函数模板可以产生更好的匹配(普通函数需要隐式类型转换),则调用函数模板
类模板
template<class NameType,class AgeType>
class Person
{
public:
Person(NameType name, AgeType age){
this->mName = name;
this->mAge = age;
}
}
- 类模板没有制定类型推导
- 类模板在模板参数列表中 可以有默认参数
- 类模板中的成员函数在创建的时候调用,普通类中的成员函数在一开始即可创建
- 做函数参数:1.指定传入类型 2.参数模板化(确定参数数据类型用
typeid(T1)) 3.整个类模板化 - 类模板的继承
template<class T>
class Base
{
T m;
}
class Son:public Base<int> //必须要知道父类的T类型,才能继承给子类
{
};
template<class T>
class Base
{
T m;
}
template<class T1,class T2>
class Son2:public Base<T2>
{
T1 obj;
};
void test(){
Son2<int,char>S2;
}
- 模板类 构造函数,成员函数 类外实现
template<class T1,class T2>
class Person{
public:
Person(T1 name, T2 age);
void showPerson();
}
template<class T1 , class T2>
Person<T1,T2>::Person(T1 name,T2 age){
this->m_Name = name;
this->m_Age = age;
}
temlate<class T1,class T2>
void Person<T1,T2>::showPerson(){
}
- 分文件编写 1.直接包含.cpp文件 2.将声明和实现写在同一个文件中,并更改后缀为hpp(约定的名称)
- 类模板与友元 1.全局函数类内实现 2.全局函数类外实现
STL
标准模板库
从广义上分为 容器 算法 迭代器
容器和算法之间通过迭代器进行无缝衔接
六大组件:容器 算法 迭代器 仿函数 适配器 空间配置器
容器:序列式容器,关联式容器。 vector string deque list set map
算法:质变算法,非质变算法
迭代器:类似于指针