一、从C到C++
1.引用
int b;
int &a = b;//a是引用类型 定义的时候加& 表示引用 其余都是取地址
a是b别名 使用a和使用b是一样的 主要用于函数传参
1.引用
int b;
int &a = b;//a是引用类型 定义的时候加& 表示引用 其余都是取地址
a是b别名 使用a和使用b是一样的 主要用于函数传参
引用和指针的区别:
引用 相当于外号 不占内存 引用是常量 定义的时候就一定要赋值 不能修改指向
指针 指针变量 存放的是地址 占内存 定义的时候并需要赋值
引用 相当于外号 不占内存 引用是常量 定义的时候就一定要赋值 不能修改指向
指针 指针变量 存放的是地址 占内存 定义的时候并需要赋值
引用 主要在函数传递参数 提高效率 效率高于指针
int &a = b;//定义的时候 是引用
&a;//取地址
&a;//取地址
C++也有指针
2.函数部分
函数 作用 : 代码封装-->减少重复代码 划分模块 便于维护
函数调用的时候-->函数跳转(需要时间)
2.1内联
关键字 inline 内联函数--> #define M(a, b) ((a)+(b))
函数 作用 : 代码封装-->减少重复代码 划分模块 便于维护
函数调用的时候-->函数跳转(需要时间)
2.1内联
关键字 inline 内联函数--> #define M(a, b) ((a)+(b))
内联和带参宏定义相似 通过代码复制(浪费内存) 减少函数跳转所需要的时间 空间膨胀换时间
内联 函数 用起来和函数一样 适用于短的函数 并且常用的函数
长的函数 循环 函数调用 不适用于内联
内联 函数 用起来和函数一样 适用于短的函数 并且常用的函数
长的函数 循环 函数调用 不适用于内联
带参宏定义什么时候用 ? -->内联什么时候用
内联-->替换带参宏定义
相对于 带参宏定义的好处 参数有类型 不需要考虑优先级 直接当函数使用
相对于 带参宏定义的好处 参数有类型 不需要考虑优先级 直接当函数使用
2.2 重载 函数名 相同 参数不一样
C++允许函数同名 参数不一样(1.参数个数 2.类型)
a + b--> int fun(int a, int b)
double fun2(double a, double b)
-->int fun(int a, int b)
double fun(double a, double b);
一个函数名实现相近多个功能 调用函数的时候通过参数区分
仅返回值不同不构成重载
计算机通过参数类型区分调用哪个函数
C++允许函数同名 参数不一样(1.参数个数 2.类型)
a + b--> int fun(int a, int b)
double fun2(double a, double b)
-->int fun(int a, int b)
double fun(double a, double b);
一个函数名实现相近多个功能 调用函数的时候通过参数区分
仅返回值不同不构成重载
计算机通过参数类型区分调用哪个函数
2.3 缺省
定义函数的时候给参数设定默认值 调用函数的时候 这个参数可以省略
函数定义的时候能不能缺省 ? 能
函数定义的时候在参数里面写
如果函数有声明 有定义 只需在声明中缺省
缺省 只能从右往左缺省(允许全部缺省)
定义函数的时候给参数设定默认值 调用函数的时候 这个参数可以省略
函数定义的时候能不能缺省 ? 能
函数定义的时候在参数里面写
如果函数有声明 有定义 只需在声明中缺省
缺省 只能从右往左缺省(允许全部缺省)
-->重载和缺省 二义性问题
调用的时候 不确定调用哪个函数-->二义性(重载和缺省同时使用的时候)
4.new delete
-->new 申请 内存 malloc
--->delete 释放内存 free
申请10个int大小的空间
int *p = (int*)malloc(sizeof(int)* 10);//C语言
free(p);//C语言的内存释放
int *p = new int[10];//C++的内存申请 10个int
delete[] p;//释放内存 []放delete后面 []里面不需要加数字 delete[] p;
int *q = new int;//申请一个int
delete q;//释放内存
调用的时候 不确定调用哪个函数-->二义性(重载和缺省同时使用的时候)
4.new delete
-->new 申请 内存 malloc
--->delete 释放内存 free
申请10个int大小的空间
int *p = (int*)malloc(sizeof(int)* 10);//C语言
free(p);//C语言的内存释放
int *p = new int[10];//C++的内存申请 10个int
delete[] p;//释放内存 []放delete后面 []里面不需要加数字 delete[] p;
int *q = new int;//申请一个int
delete q;//释放内存
int m = 10;
int *p = new int[m];//申请的时候[]里面的数字是多少个变量 m个int的大小的空间
int *p = new int[m];//申请的时候[]里面的数字是多少个变量 m个int的大小的空间
struct node{};
struct node*p = new node;//申请内存
5.简单的输入输出
cin cout 输入输出 endl 换行
cin >>
cout <<
struct node*p = new node;//申请内存
5.简单的输入输出
cin cout 输入输出 endl 换行
cin >>
cout <<
cin >> 不需要加取地址
C++兼容C 然后C语言不能使用C++的
二、面向对象
1.面向对象的思想
面向过程 1.创建界面 2.生成地图 3.贴图 4.用户点击 5.判断
以函数为中心
面向对象 用对象描述-->对象-->数据为核心
抽象 封装 继承 多态
1.面向对象的思想
面向过程 1.创建界面 2.生成地图 3.贴图 4.用户点击 5.判断
以函数为中心
面向对象 用对象描述-->对象-->数据为核心
抽象 封装 继承 多态
2.类和对象
C语言 结构体
struct student
{
//成员变量
};
C++的类
class student
{
//成员变量
//成员函数
};
对象用.对象指针 ->
C语言 结构体
struct student
{
//成员变量
};
C++的类
class student
{
//成员变量
//成员函数
};
对象用.对象指针 ->
对象(数据 行为 保密的)---> 权限问题
public 公有 外部可以直接访问 所有人都可以用
private 私有 类的内部进行访问 但是外部不能访问
protected 受保护的 和私有类似
类中的成员变量 成员函数 默认私有
public 公有 外部可以直接访问 所有人都可以用
private 私有 类的内部进行访问 但是外部不能访问
protected 受保护的 和私有类似
类中的成员变量 成员函数 默认私有
数据方面 一般写成私有 函数 公有
成员函数中都有一个this指针(和普通函数区别) 指针指向当前对象
区分参数和成员(如果参数名和成员名不一样 那么可以省略this指针)
成员函数中都有一个this指针(和普通函数区别) 指针指向当前对象
区分参数和成员(如果参数名和成员名不一样 那么可以省略this指针)
//成员变量 私有 -->在外部调用公有的成员函数给成员变量赋值
类的成员函数并不是一定要在类里面定义 可以在类外定义 在类里面声明
类的成员函数并不是一定要在类里面定义 可以在类外定义 在类里面声明
3.类和结构体的区别和联系
C++ 的结构体中可以有成员函数(C语言结构体不行) private / protected / public C++结构体定义变量的时候不需要加struct关键字
struct student pointer;
C++ 的结构体中可以有成员函数(C语言结构体不行) private / protected / public C++结构体定义变量的时候不需要加struct关键字
struct student pointer;
C++类
区别 结构体成员默认公有 类的成员默认私有
结构体 一般只用来存放数据 类中有函数
区别 结构体成员默认公有 类的成员默认私有
结构体 一般只用来存放数据 类中有函数
理解类的本质-->结构体 公有的类 类就是私有的结构体
C++结构体中也可以写函数
对象大小只和成员变量的大小有关
C++结构体中也可以写函数
对象大小只和成员变量的大小有关
三、构造析构拷贝
1.构造函数
函数给对象成员初始化-->构造函数(构造器)
构造函数 1.和类名相同 2.没有返回值类型 也没有返回值 3.调用构造的方式 对象出生自动调用(只会调用一次)
类中成员 没办法在类外访问->使用的时候赋默认值 初始化
函数名和参数-->重载 缺省
1.构造函数
函数给对象成员初始化-->构造函数(构造器)
构造函数 1.和类名相同 2.没有返回值类型 也没有返回值 3.调用构造的方式 对象出生自动调用(只会调用一次)
类中成员 没办法在类外访问->使用的时候赋默认值 初始化
函数名和参数-->重载 缺省
2.析构函数
析构函数 释放资源(比如说类中的指针 申请内存 交给析构释放_)
析构函数名 ~类名 没有返回值类型, 没有参数 自动调用(对象销毁的时候调用)
析构函数 释放资源(比如说类中的指针 申请内存 交给析构释放_)
析构函数名 ~类名 没有返回值类型, 没有参数 自动调用(对象销毁的时候调用)
系统提供的默认构造(没有参数)和析构(不会做任何事情)
3.new delete malloc free
new 调用构造 malloc不会
delete 调用析构 free不会
delete[] 调用所有对象的析构函数
new 调用构造 malloc不会
delete 调用析构 free不会
delete[] 调用所有对象的析构函数
4.const成员和初始化形参列表-->给变量赋值
const 常属性 只能通过初始化形参列表赋值
初始化形参列表的写法
初始化形参列表可以给const成员赋值 也可以给普通成员变量赋值
const 常属性 只能通过初始化形参列表赋值
初始化形参列表的写法
初始化形参列表可以给const成员赋值 也可以给普通成员变量赋值
5.static 成员
不属于某个对象 属于整个类
这个类的所有对象共用static属性成员
类--> class --->定义的变量叫做对象 作用域:全局
不属于某个对象 属于整个类
这个类的所有对象共用static属性成员
类--> class --->定义的变量叫做对象 作用域:全局
* 设定初值 类外赋值
6.拷贝构造
1.类-->提供4个默认函数
默认构造函数(没有参数的构造函数)
析构函数
拷贝构造
赋值函数
1.类-->提供4个默认函数
默认构造函数(没有参数的构造函数)
析构函数
拷贝构造
赋值函数
拷贝构造 特殊的构造函数-->参数只有一个 是这个类类型的参数
*调用时机 1)定义对象的时候 A a(A类型的对象)
2)定义对象的时候 用 = 初始化 A a = A类型的对象;
3)隐性调用 传递参数 形参拷贝实参
*调用时机 1)定义对象的时候 A a(A类型的对象)
2)定义对象的时候 用 = 初始化 A a = A类型的对象;
3)隐性调用 传递参数 形参拷贝实参
注意的点 : 1.参数必须为引用类型 (如果不使用引用 那么传参的过程会调用拷贝构造)
2.拷贝构造中 单独申请内存-->深拷贝
没有单独申请内存-->浅拷贝
浅拷贝-->两个指针指向同一个区域 释放内存会出问题
默认的拷贝构造是浅拷贝
2.拷贝构造中 单独申请内存-->深拷贝
没有单独申请内存-->浅拷贝
浅拷贝-->两个指针指向同一个区域 释放内存会出问题
默认的拷贝构造是浅拷贝
对象传递参数 引用 没有新的对象产生
没有写引用 有一个临时对象
对象 一般传参都会传递引用
没有写引用 有一个临时对象
对象 一般传参都会传递引用
2.const成员(成员变量 / 成员函数)
const 常属性 const int x定义必须赋初值 赋值之后不能修改
类中的const成员变量 赋值初始化形参列表赋值
const成员函数 在成员函数后面加const 里面不能修改类成员变量的值
不能调用其他非const成员函数
const加在函数前面 修饰返回值类型
const加在函数的括号里面 修饰 参数类型
const 常属性 const int x定义必须赋初值 赋值之后不能修改
类中的const成员变量 赋值初始化形参列表赋值
const成员函数 在成员函数后面加const 里面不能修改类成员变量的值
不能调用其他非const成员函数
const加在函数前面 修饰返回值类型
const加在函数的括号里面 修饰 参数类型
引用 提高效率 可以在函数中通过引用修改实参的值
传递const引用 可以避免在函数修改实参的值
--> const int*和 int*const
const int*p 常量指针 *p的值不可以修改但指向可改变//int const *p
int*const q 指针常量 q的值能修改但q不能改指向
3.static成员(成员变量 / 成员函数)
1)修饰全局变量 不能在外部文件访问(extern指明访问访问其他cpp的变量)
2)修饰函数里面的变量 只会定义一次(静态存储区 函数调用结束先不释放)
3)修饰成员变量 static成员变量 不属于单个对象 属于整个类(赋初值 要在类外赋初值)
4)修饰成员函数 也是属于这个类 只能修改静态成员
(static修饰成员函数 没有this指针)
传递const引用 可以避免在函数修改实参的值
--> const int*和 int*const
const int*p 常量指针 *p的值不可以修改但指向可改变//int const *p
int*const q 指针常量 q的值能修改但q不能改指向
3.static成员(成员变量 / 成员函数)
1)修饰全局变量 不能在外部文件访问(extern指明访问访问其他cpp的变量)
2)修饰函数里面的变量 只会定义一次(静态存储区 函数调用结束先不释放)
3)修饰成员变量 static成员变量 不属于单个对象 属于整个类(赋初值 要在类外赋初值)
4)修饰成员函数 也是属于这个类 只能修改静态成员
(static修饰成员函数 没有this指针)
四、运算符重载
1.友元
数据--->私有 不能在类外访问 安全性
访问里面数据的时候-->通过函数访问 速度慢
针对常用的,需要使用类中私有成员的函数-->友元
友元 可以访问类中 的私有成员
friend 关键字 放在函数前面-->破坏数据封装性
1.友元
数据--->私有 不能在类外访问 安全性
访问里面数据的时候-->通过函数访问 速度慢
针对常用的,需要使用类中私有成员的函数-->友元
友元 可以访问类中 的私有成员
friend 关键字 放在函数前面-->破坏数据封装性
类中成员 私有成员-->只能类中成员函数 友元函数访问
公有成员-->所有函数都能访问
友元函数 是类的成员函数么 ? 不是
公有成员-->所有函数都能访问
友元函数 是类的成员函数么 ? 不是
友元的优缺点
友元 外部访问私有成员的机制
通过成员函数访问私有成员-->友元提高 运行效率
缺点 破坏数据的封装性 导致程序的可维护性变差
友元 外部访问私有成员的机制
通过成员函数访问私有成员-->友元提高 运行效率
缺点 破坏数据的封装性 导致程序的可维护性变差
友元函数:将一个函数声明成一个类的友元 这个函数可以访问类的私有成员
友元类:将一个类A声明成另外一个类B的友元 这个A中所有成员函数可以访问类B的私有成员
A是B的朋友 B是不是A的朋友 ? 不是
我把你当朋友 你把我当(...自行脑补)
A是B的朋友 B是C的朋友 A是不是C的朋友 ? 不是
---> 友元类 不能传递
友元类:将一个类A声明成另外一个类B的友元 这个A中所有成员函数可以访问类B的私有成员
A是B的朋友 B是不是A的朋友 ? 不是
我把你当朋友 你把我当(...自行脑补)
A是B的朋友 B是C的朋友 A是不是C的朋友 ? 不是
---> 友元类 不能传递
类的私有属性-->类中成员的访问方式
私有成员不能在类外访问 不影响友元
私有成员不能在类外访问 不影响友元
友元的声明和定义可以放在类中的任何位置
关于 友元类使用 一般不会用到
类中的函数 声明成另外一个类的友元
类中的函数 声明成另外一个类的友元
2.运算符重载
运算符 基本数据类型使用运算符-->在自己定义的对象之间使用运算符
PS:运算符重载不是一定要写 写了之后可以实现在自己的对象之间使用运算符操作
不能重载的运算符 . :: .* sizeof 三目运算符
访问对象中的数据 1.成员函数 2.友元函数 --->a + b
operator运算符(参数)
运算符重载 函数参数个数必须和操作数个数一致
函数的实现 不要违反运算规则
优先级问题:重载时候不要改变优先级顺序
不可以创造新的运算符
访问对象中的数据 1.成员函数 2.友元函数 --->a + b
operator运算符(参数)
运算符重载 函数参数个数必须和操作数个数一致
函数的实现 不要违反运算规则
优先级问题:重载时候不要改变优先级顺序
不可以创造新的运算符
单目--->推荐成员函数
双目-->推荐写友元函数 两个参数可以交换顺序
双目-->推荐写友元函数 两个参数可以交换顺序
(1)算数运算符 + -*/ %
(2)关系运算符 == != > < >= <= 双目运算符
(3)逻辑运算符 && || !
(4)自增 自减(前++ 后++)
(5)位运算符
(6)赋值运算符 += -= (= 额外讲)
(7) ()[] * & ->
()--> A a; a();//调用的重载()运算符 函数调用 函数名(参数)
重载()的目的 让这个对象更像函数 仿函数 对象-->模仿函数
(2)关系运算符 == != > < >= <= 双目运算符
(3)逻辑运算符 && || !
(4)自增 自减(前++ 后++)
(5)位运算符
(6)赋值运算符 += -= (= 额外讲)
(7) ()[] * & ->
()--> A a; a();//调用的重载()运算符 函数调用 函数名(参数)
重载()的目的 让这个对象更像函数 仿函数 对象-->模仿函数
仿函数--->实现方式 运算符重载 重载()
对象名(参数) 仿函数 调用方式和函数形式一样
1)仿函数 参数 仿函数 返回值
构造 在对象出生时调用 只会调用
仿函数 在对象出生之后调用 想调用几次就调用几次
对象名(参数) 仿函数 调用方式和函数形式一样
1)仿函数 参数 仿函数 返回值
构造 在对象出生时调用 只会调用
仿函数 在对象出生之后调用 想调用几次就调用几次
// 返回值类型 operator() (参数)
回调函数-->仿函数
[]--> string-->里面存了字符串 string st("hello"); 假装是数组 char*
-->st[3]-->'l'
重载[] 对象中放数组 / 指针(申请动态数组) int数组
* 里面有指针
& 返回某个成员的地址
-> 里面有结构体 / 对象 成员 返回结构体 / 对象的地址 struct
(8)new delete
new运算符 三个操作
1)operator new (可以重载的部分)主要作用 分配内存
2)调用构造函数
3)返回一个指针
[]--> string-->里面存了字符串 string st("hello"); 假装是数组 char*
-->st[3]-->'l'
重载[] 对象中放数组 / 指针(申请动态数组) int数组
* 里面有指针
& 返回某个成员的地址
-> 里面有结构体 / 对象 成员 返回结构体 / 对象的地址 struct
(8)new delete
new运算符 三个操作
1)operator new (可以重载的部分)主要作用 分配内存
2)调用构造函数
3)返回一个指针
delete 两个操作
1)调用析构
2)释放内存 operator delete (可以重载的部分)
1)调用析构
2)释放内存 operator delete (可以重载的部分)
operator new 成员函数 分配内存 new 调用的时机还没有对象 只能static 全局 不能成员(想想前面讲到的static)
* operator new() 和 operator delete() 是隐式的static成员。因此,它们无法使用this指针,也不能修改对象的值。一般不建议改写。
* operator new() 和 operator delete() 是隐式的static成员。因此,它们无法使用this指针,也不能修改对象的值。一般不建议改写。
#include<iostream>
using namespace std;
class String
{
private:
char* m_data;
size_t length;
public:
//构造函数
String(const char *str=NULL)
{
if (str == NULL)
{
length = ;
m_data = new char[];
*m_data = '\0';
}
else
{
length = strlen(str);
m_data = new char[length + ];
strcpy(m_data, str);
}
}
//构析函数
~String()
{
delete[] m_data;
length = ;
}
//拷贝构造
String(const String &other)
{
length = strlen(other.m_data);
m_data = new char[length + ];
strcpy(m_data, other.m_data);
} //获取字符串长度
size_t size()const
{
return length;
} // [] 重载
char& operator[](int n)const
{
if (n >= length)
return m_data[length - ];
else
return m_data[n];
}
// = 重载
String& operator=(const String &other)
{
if (this == &other)
return *this;
if (m_data)
delete[] m_data;
length = strlen(other.m_data);
m_data = new char[length + ];
strcpy(m_data, other.m_data);
return *this;
}
// + 重载
String operator+(const String& str)const
{
String newstr;
newstr.length = length + str.size();
newstr.m_data = new char[newstr.length + ];
strcpy(newstr.m_data, m_data);
strcat(newstr.m_data, str.m_data);
return newstr;
}
// += 重载
String& operator+=(const String& str)
{
length += str.length;
char* newdata = new char[length + ];
strcpy(newdata, m_data);
strcat(newdata, str.m_data);
delete[] m_data;
m_data = newdata;
return *this;
}
// == 重载
bool operator==(const String& str)const
{
if (length != str.length)
return false;
return strcmp(m_data, str.m_data) ? false : true;
}
// >> 重载
friend istream& operator>>(istream& is, String& str)
{
is >> str.m_data;
return is;
}
// << 重载
friend ostream& operator<<(ostream& os, String& str)
{
os << str.m_data;
return os;
}
// < 重载
friend bool operator <(const String& s1, const String& s2)
{
if (strcmp(s1.m_data, s2.m_data) < )
return true;
return false;
}
// > 重载
friend bool operator >(const String& s1, const String& s2)
{
if (strcmp(s1.m_data, s2.m_data) > )
return true;
return false;
} }; class A
{
int x,*y;
A *p;
public:
A(int m)
{
x = m;
y = &x;
}
A(const A&other)
{ }
~A()
{ } A& operator&(A&other)
{
this->y = &other.x;
return *this;
}
friend A operator*(A&other)
{
return *(other.y);
}
A* operator->()
{
return p;
}
friend ostream&operator<<(ostream&os, A&other)
{
int q = *other.y;
os << q;
return os;
}
};
int main()
{
String str1("hello"),str2("world");
cout << str1 << endl << str2 << endl << str1 + str2 << endl;
str1 += str2;
cout << str1 << endl;
A a(),b(),c();
int x;
cout << (a&b) << endl << (*c=a) << endl;
return ;
}
五、继承
继承 从已有类中加上属性(成员变量)和方法(成员函数)定义一个新的类型
已有类 基类 / 父类 新的类 派生类 / 子类
父类派生子类 子类继承父类
继承 从已有类中加上属性(成员变量)和方法(成员函数)定义一个新的类型
已有类 基类 / 父类 新的类 派生类 / 子类
父类派生子类 子类继承父类
1.继承 子类得到父类的所有成员和所有函数(不包括构造和析构)
2.调整
2.1访问控制
父类的成员访问权限 public private protected
继承方式
public public 不可访问 protected
private private 不可访问 private
protected protected 不可访问 protected
(子类成员的访问权限)
继承方式 默认私有
2.调整
2.1访问控制
父类的成员访问权限 public private protected
继承方式
public public 不可访问 protected
private private 不可访问 private
protected protected 不可访问 protected
(子类成员的访问权限)
继承方式 默认私有

父类 成员属性--->继承方式不同--->继承到子类中 在子类中的表现不一样
父类的私有成员在子类中不能访问
子类继承过来的父类的成员函数去访问
父类的私有成员在子类中不能访问
子类继承过来的父类的成员函数去访问
protected 子类继承过来可以访问
private 子类继承过来不能访问
private 子类继承过来不能访问
2.2 隐藏 子类存在和父类的同名的成员(变量/函数) 子类优先使用子类的成员
父类的成员得到隐藏
想要访问父类的同名函数 用类名
父类的成员得到隐藏
想要访问父类的同名函数 用类名
2.3 添加新成员 子类 添加新功能 添加新成员变量/成员函数--->实现新的功能
3.多继承 多个父类派生一个子类(C++有 其他语言没有)
多继承怎么写
子类拥有所有父类中所有的成员
3.多继承 多个父类派生一个子类(C++有 其他语言没有)
多继承怎么写
子类拥有所有父类中所有的成员
4.关于多继承中
多个父类有同名成员变量 成员函数(参数)
子类中新定义的成员和父类成员同名(隐藏)
多个父类有同名成员变量 成员函数(参数)
子类中新定义的成员和父类成员同名(隐藏)
5.菱形继承问题
A派生B和C B和C共同派生D D有两份来自爷爷中的成员
解决菱形继承方法-->虚继承 virtual
//解决方案 让B和C 虚继承A(两个子类都要virtual)
//实现的机制
孙子类 就一份来自祖父类的拷贝
A派生B和C B和C共同派生D D有两份来自爷爷中的成员
解决菱形继承方法-->虚继承 virtual
//解决方案 让B和C 虚继承A(两个子类都要virtual)
//实现的机制
孙子类 就一份来自祖父类的拷贝
菱形继承--->影响的是哪个类 孙子类
虚继承--->解决菱形继承的问题---->孙子类
虚继承需要写在father中
虚继承--->解决菱形继承的问题---->孙子类
虚继承需要写在father中
A(x, y)-->B(A(x1, y1), z) C(A(x2, y2), z)
B + C---->D(B(A(x, y), z), C(A(x, y), z), z) 菱形继承
虚继承-->D(B(z), C(z), A(x, y), z)// B::z C::z z
PS:这里还涉及到一个知道点----偏移量,有兴趣先自己去了解,暂时不做讲解
B + C---->D(B(A(x, y), z), C(A(x, y), z), z) 菱形继承
虚继承-->D(B(z), C(z), A(x, y), z)// B::z C::z z
PS:这里还涉及到一个知道点----偏移量,有兴趣先自己去了解,暂时不做讲解
#include<iostream>
using namespace std; class people
{
protected:
int age;
int height;
int ID;
char name[];
public:
people(){ cout << "调用people构造函数" << endl; }
~people(){ cout << "调用people构析" << endl; }
};
class man :virtual public people
{
protected:
char sex;
int num;
public:
man(){ cout << "调用man构造函数" << endl; }
~man(){ cout << "调用man构析" << endl; }
};
class student :virtual public people
{
protected:
int num;
int stuID;
public:
student(){ cout << "调用student构造" << endl; }
~student(){ cout << "调用student构析" << endl; }
}; class maleStu :public man, public student
{
int num;
public:
maleStu(){ cout << "调用maleStu构造" << endl; }
~maleStu(){ cout << "调用maleStu构析" << endl; }
void fun()
{ } }; class maleStu1 :public student, public man
{
int num;
public:
maleStu1(){ cout << "调用maleStu构造" << endl; }
~maleStu1(){ cout << "调用maleStu构析" << endl; }
void fun()
{ } }; int main()
{
{
maleStu a;
a.fun();
}
cout << endl;
{
maleStu1 b;
b.fun();
} cin.get();
return ;
} /*
结果:
多继承中 父类构造函数按照继承顺序进行调用在前的先调用在后的后调用
虚继承中 孙子类的构造函数调用父类和爷爷类的构造函数 顺序 爷爷类->父类1->父类2->...->孙子类 */
六、多态
多态 一种形式 多种状态
想想USB接口 每种线功能不一样-->多态
多态 一种形式 多种状态
想想USB接口 每种线功能不一样-->多态
父类的指针 可以指向子类对象 什么类的指针 调用什么类的函数
一个基本保证:一个指针永远不应指向这样的对象——不能提供指针所承诺的最基本的属性
一个基本保证:一个指针永远不应指向这样的对象——不能提供指针所承诺的最基本的属性
目的 通过父类的指针调用子类的函数 实现方式:虚函数
virtual 虚函数 在父类的函数名前面加上virtual 子类中重写父类的虚函数(函数名相同 参数一样)
覆盖 子类重写父类的虚函数 父类的函数得到覆盖(用父类指针调用 调用的也是子类函数)
隐藏 子类重写父类的函数 父类的函数得到隐藏
覆盖 子类重写父类的虚函数 父类的函数得到覆盖(用父类指针调用 调用的也是子类函数)
隐藏 子类重写父类的函数 父类的函数得到隐藏
虚函数-->根据对象决定调用什么函数
父类 虚函数-->没必要写 父类虚函数 不写实现 直接函数后面写上 = 0 纯虚函数
//virtual void eat() = 0;// 纯虚函数
*一个有纯虚函数的类(虚类 / 抽象类 / 接口类) 没办法定义对象 只能定义指针
*如果子类继承了父类的纯虚函数 然后没有实现 那么这个子类也是虚类 不能创建对象
//virtual void eat() = 0;// 纯虚函数
*一个有纯虚函数的类(虚类 / 抽象类 / 接口类) 没办法定义对象 只能定义指针
*如果子类继承了父类的纯虚函数 然后没有实现 那么这个子类也是虚类 不能创建对象
构造和析构 构造(不用管)
析构 建议写虚析构
析构 建议写虚析构
//知道虚表存在-->多出一个指针大小 //涉及偏移量
//虚函数 类中定义了新的虚函数 类中会多出来一个指针(虚表指针)
//虚函数 类中定义了新的虚函数 类中会多出来一个指针(虚表指针)
虚函数访问方式 通过虚表访问
实现多态的方式 子类通过重写 修改虚表中函数的地址
实现多态的方式 子类通过重写 修改虚表中函数的地址
father*p = new son-->p能访问到的内容 1.father的成员函数 2.son的成员(继承过去的成员)
继承-->子类重写父类的函数(成员函数)
虚函数 1.成员函数
2.不能是静态成员函数
3.不能是内联函数(会处理成非内联函数) inline (PS: 类中实现的成员 默认处理成内联函数)
4.构造不能为虚函数 析构一般写虚函数
2.不能是静态成员函数
3.不能是内联函数(会处理成非内联函数) inline (PS: 类中实现的成员 默认处理成内联函数)
4.构造不能为虚函数 析构一般写虚函数
#include<iostream>
using namespace std;
class father
{
protected:
int x;
public:
father();
virtual void fun()//虚函数
{
cout << "父类的函数" << endl;
}
virtual void eat() = ;// 纯虚函数
virtual ~father(){} };
//多个函数 father::father()
{ } class son :public father
{
public:
void fun()
{
cout << "子类的函数" << endl;
}
void eat()
{
//
}
};
class son2 :public father
{
public:
void fun();
//{
// cout << "hello world" << endl;
//}
};
void son2::fun()
{
cout << "hello world" << endl;
}
void fun(father *p)//形参是父类指针 实参 是子类对象的地址
{
p->fun();
}
int main()
{
//son ss;
//father*p = &ss;
//p->fun();
//cout << sizeof(ss) << endl;
//x.fun();
//father fa;
son s1;//调用的是子类的构造函数
//son2 s2;
fun(&s1);
//fun(&s2);
cout << sizeof(son) << endl;
father *p= new son;//调用的是 子类的构造函数
delete p;//调用什么的析构函数 析构函数写成虚函数 cin.get();
return ;
}
七、I/O流
cin / cout 输入输出流
文件流
ifstream 输入文件流 从文件中读取内容
ofstream 输出文件流 输出内容到文件
cin / cout 输入输出流
文件流
ifstream 输入文件流 从文件中读取内容
ofstream 输出文件流 输出内容到文件
fstream 文件流 可以输入可以输出*
字符流 当作字符处理 txt文本文件 直接打开不会乱码
字节流 存二进制的方式 直接打开文件 可能会乱码
字节流 存二进制的方式 直接打开文件 可能会乱码
C语言 FILE *fp 文件指针和文件操作相关联
C++文件操作 对象和文件绑定
C++文件操作 对象和文件绑定
成员函数
open 打开文件 close 关闭
is_open 判断是否打开成功
读取和写入
字符流 get put
字节流 read write
>> 从文件中读取数据 << 写入文件
文件指针
输入的文件流指针 seekg tellg
输出的文件流指针 seekp tellp exe-->文件
open 打开文件 close 关闭
is_open 判断是否打开成功
读取和写入
字符流 get put
字节流 read write
>> 从文件中读取数据 << 写入文件
文件指针
输入的文件流指针 seekg tellg
输出的文件流指针 seekp tellp exe-->文件
//fstream file;//创建一个文件对象
//file.open("1.txt", ios::in | ios::out | ios::binary);//打开文件 文件名+打开方式
//ios::in 读取 ios::out写入 ios::app 追加 ios::binary二进制方式打开
//file.get(ch);//从文件中读取一个字符
//file.getline(arr,100); //从文件中读取一行字符
//file.put(ch);//写入一个字符到文件
//file.put('h');
//file.write("hello world", 12);//写入文件 第一个参数char* 第二个参数代表大小
//file.read(arr, 12);//从文件中读出 第一个参数 读出来之后放的位置 第二个参数 大小
//file << "hello world";//写入文件
//file >> arr;//读取文件
//std::streamoff x = file.tellp();//返回文件指针当前位置距离文件开头的偏移量
//file.seekp(x, ios::beg);//第一个参数 偏移量 第二个参数 文件相对位置
//ios::beg 文件开始 ios::end 文件末尾 ios::cur 文件指针当前位置
//file.open("1.txt", ios::in | ios::out | ios::binary);//打开文件 文件名+打开方式
//ios::in 读取 ios::out写入 ios::app 追加 ios::binary二进制方式打开
//file.get(ch);//从文件中读取一个字符
//file.getline(arr,100); //从文件中读取一行字符
//file.put(ch);//写入一个字符到文件
//file.put('h');
//file.write("hello world", 12);//写入文件 第一个参数char* 第二个参数代表大小
//file.read(arr, 12);//从文件中读出 第一个参数 读出来之后放的位置 第二个参数 大小
//file << "hello world";//写入文件
//file >> arr;//读取文件
//std::streamoff x = file.tellp();//返回文件指针当前位置距离文件开头的偏移量
//file.seekp(x, ios::beg);//第一个参数 偏移量 第二个参数 文件相对位置
//ios::beg 文件开始 ios::end 文件末尾 ios::cur 文件指针当前位置
1.输入输出格式控制
输入 cin
cin >> x;
输入 cin
cin >> x;
函数名-->cin.get.成员运算符 cin cout 对象
int cin.get();//得到一个字符 getchar()
istream& cin.get(char&ch);
istream& cin.get(char*arr, streamsize n);//arr是数组名 n是大小
istream& cin.get(char*arr, streamsize n, char end);//读取到这个end这个字符 提前结束
int cin.get();//得到一个字符 getchar()
istream& cin.get(char&ch);
istream& cin.get(char*arr, streamsize n);//arr是数组名 n是大小
istream& cin.get(char*arr, streamsize n, char end);//读取到这个end这个字符 提前结束
cin.getline 读取一行的字符 //读取字符串
istream& cin.getline(char*arr, streamsize n);//arr是数组名 n是大小
istream& cin.getline(char*arr, streamsize n, char end);//读取到这个end这个字符 提前结束
istream& cin.getline(char*arr, streamsize n);//arr是数组名 n是大小
istream& cin.getline(char*arr, streamsize n, char end);//读取到这个end这个字符 提前结束
cout
iomanip头文件
hex / oct / dec 16进制 / 8进制 / 10进制 默认是10进制 cout << hex << 233 << endl;
2.精度控制
setprecision(4)//设置小数的位数
setiosflags(ios::fixed);//固定小数位
cout << setiosflags(ios::fixed) << setprecision(4) << 3.141592654 << endl;
cout << fixed << setprecision(5) << 3.141592654 << endl;
//用指数方式 setiosflags(ios::scientific)
3.宽度控制 左右对齐
setw(n) 设置宽度 最小输出的位数 set width
setiosflags(ios::left)//左对齐
setiosflags(ios::right)//右对齐
iomanip头文件
hex / oct / dec 16进制 / 8进制 / 10进制 默认是10进制 cout << hex << 233 << endl;
2.精度控制
setprecision(4)//设置小数的位数
setiosflags(ios::fixed);//固定小数位
cout << setiosflags(ios::fixed) << setprecision(4) << 3.141592654 << endl;
cout << fixed << setprecision(5) << 3.141592654 << endl;
//用指数方式 setiosflags(ios::scientific)
3.宽度控制 左右对齐
setw(n) 设置宽度 最小输出的位数 set width
setiosflags(ios::left)//左对齐
setiosflags(ios::right)//右对齐
setfill(ch) 设置填充字符
输入输出缓冲区
输入 输出
输入数据之后(炒饭)-->按了回车 进入缓冲区(上菜)---->程序读取缓冲区的内容(夹菜)
输入10个字符-->读取了5个字符 剩下5个字符留给下一次 输入缓冲区
回车作为结束-->while ((ch = getchar()) != '\n'&&ch != EOF);//清空缓冲区
输入 输出
输入数据之后(炒饭)-->按了回车 进入缓冲区(上菜)---->程序读取缓冲区的内容(夹菜)
输入10个字符-->读取了5个字符 剩下5个字符留给下一次 输入缓冲区
回车作为结束-->while ((ch = getchar()) != '\n'&&ch != EOF);//清空缓冲区
输出缓冲区 有些编译器 输出 不是立刻输出这个内容-->需要刷新缓冲区 fflush
printf("输入字符");//提示用户输入什么内容
scanf("%c", &ch);
printf("输入字符");//提示用户输入什么内容
scanf("%c", &ch);
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
char a[];
int main()
{
fstream flie;
flie.open("1.txt", ios::in | ios::out|ios::binary);
if (!flie.is_open())
{
cout << "文件打开失败" << endl;
return ;
} //string a;
//flie >> a; flie.seekp(, ios::end);
streamoff x = flie.tellp();
int y = (int)x;
flie.seekp(, ios::beg);
flie.read(a, y);
for (int i = ; i < y; i++)
{
a[i] += ;
}
//cout << a;
flie.seekp(,ios::beg);
flie.clear();
flie.write(a, y);
flie.close();
return ;
}
简单加密
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
char a[];
int main()
{
fstream flie;
flie.open("1.txt", ios::in | ios::out | ios::binary);
if (!flie.is_open())
{
cout << "文件打开失败" << endl;
return ;
} //string a;
//flie >> a; flie.seekp(, ios::end);
streamoff x = flie.tellp();
int y = (int)x;
flie.seekp(, ios::beg);
flie.read(a, y);
for (int i = ; i < y; i++)
{
a[i] -= ;
}
//cout << a;
flie.seekp(, ios::beg);
flie.clear();
flie.write(a, y);
flie.close();
return ;
}
简单解密
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
int main()
{
fstream file1,file2,file3;
string arr1,arr2;
file1.open("1.txt", ios::in);
file2.open("2.txt", ios::in);
file3.open("3.txt", ios::out);
if (!file1.is_open() && !file2.is_open() && !file3.is_open())
{
cout << "文件打开失败" << endl;
return ;
}
char arr[];
file1 >> arr;
file3 << arr;
file2 >> arr;
file3 << arr;
file1.close();
file2.close();
file3.close();
return ;
}
八、异常
程序执行期间产生的问题--->(代码没问题)
try catch throw 三个关键字
try 尝试
catch 捕获 异常
throw 抛出异常
catch 捕获 异常
throw 抛出异常
try尝试运行代码(代码可能出现异常), 如果遇到异常 throw抛出异常 catch去捕获并且处理
1.如果异常没有处理 -->调用abort()函数 中断程序
2.throw 可以在任意地方抛出异常
3.多级异常处理 里面的异常自己不能处理 继续往外丢
4.catch(...) 表示接受任意类型的异常
1.如果异常没有处理 -->调用abort()函数 中断程序
2.throw 可以在任意地方抛出异常
3.多级异常处理 里面的异常自己不能处理 继续往外丢
4.catch(...) 表示接受任意类型的异常
try
{
try
{
throw "hello";
}
catch (int x)
{
}
}
catch (int x){}
{
try
{
throw "hello";
}
catch (int x)
{
}
}
catch (int x){}
九、模板
重载-->一个函数名 多个函数
模板 数组 链表 顺序表
1.模板分为 函数模板 类模板
template
2.函数模板
add 两个数字相加 针对仅仅是参数类型不同的函数
使用
函数模板--->传递实际类型-->模板函数
重载-->一个函数名 多个函数
模板 数组 链表 顺序表
1.模板分为 函数模板 类模板
template
2.函数模板
add 两个数字相加 针对仅仅是参数类型不同的函数
使用
函数模板--->传递实际类型-->模板函数
3.类模板 主要针对 成员类型不同的类
使用
类模板--->传入实际类型--->模板类--->用模板类创建的对象
需要强调的部分
(1)类型必须指明
(2)成员函数在类外定义必须单独加上模板头
(3) 在.h里面写模板类 函数在类外定义 不要放cpp里面 直接放.h里面
类模板--->传入实际类型--->模板类--->用模板类创建的对象
需要强调的部分
(1)类型必须指明
(2)成员函数在类外定义必须单独加上模板头
(3) 在.h里面写模板类 函数在类外定义 不要放cpp里面 直接放.h里面
十、命名空间
重载时,函数名一样参数也一样会怎么样? 重定义
该怎么办? 命名空间
重载时,函数名一样参数也一样会怎么样? 重定义
该怎么办? 命名空间
管理 类型或者数据或者函数 的方式
namespace summer//定义
{
//里面写函数或者变量
}
使用
1. using namespace 命名空间的名字; //最简单 但是不安全
后面直接使用函数或者变量
2. 命名空间名::函数名() //::域名解析符
指出使用哪个命名空间的函数 安全 但是最麻烦
3. 直接using std::cin; 后面直接使用cin就行 安全 比较简单
命名空间 可以嵌套
namespace summer//定义
{
//里面写函数或者变量
}
使用
1. using namespace 命名空间的名字; //最简单 但是不安全
后面直接使用函数或者变量
2. 命名空间名::函数名() //::域名解析符
指出使用哪个命名空间的函数 安全 但是最麻烦
3. 直接using std::cin; 后面直接使用cin就行 安全 比较简单
命名空间 可以嵌套
补充:
1.对象什么时候结束生命周期
作用域{} 出了作用域,对象结束生命周期
2.析构只释放构造函数中的指针
一般只用于对象内部指针
3.返回值为引用 和不为引用的区别 是否返回一个临时对象
结论 如果要进行连续操作 比如++++++a 返回引用 a = b = c = d = e 返回引用
4.父类构造析构和子类的构造析构一样么?? 不一样
子类需要写自己的构造和析构
子类调用构造函数的时候 需要先调用父类的构造及函数
子类调用析构之后 调用父类的析构函数
如果没有显性调用父类构造 会调用默认父类的构造函数
5.虚继承之后 只有一份拷贝 祖父类成员在孙子类成员中只有一份拷贝
1.对象什么时候结束生命周期
作用域{} 出了作用域,对象结束生命周期
2.析构只释放构造函数中的指针
一般只用于对象内部指针
3.返回值为引用 和不为引用的区别 是否返回一个临时对象
结论 如果要进行连续操作 比如++++++a 返回引用 a = b = c = d = e 返回引用
4.父类构造析构和子类的构造析构一样么?? 不一样
子类需要写自己的构造和析构
子类调用构造函数的时候 需要先调用父类的构造及函数
子类调用析构之后 调用父类的析构函数
如果没有显性调用父类构造 会调用默认父类的构造函数
5.虚继承之后 只有一份拷贝 祖父类成员在孙子类成员中只有一份拷贝