C++与C的区别(一)
头文件与命名空间
创建源文件是.cpp 文件,头文件不变,依旧是.h文件
包含头文件的方式
包含自己的头文件没有任何区别 #include "myHead.h"
包含库目录
C语言中的: 采用原来方式包含可以,可以用C++包含方式,去掉.h 加个c
iostream C++标准输入输出流的头文件
命名空间的引入
命名语法
//1.基本创建空间方法 namespace 空间名 { int a; void print(){} } //2.学会访问命名空间中的东西 //2.1 用空间名限定去访问 int main() { 空间名::a=1001; 空间名::print(); return 0; }可以增加标识符的使用率,C语言同一个作用域下不允许定义相同的标识符
作用域分辨符::
空间名限定,类名限定(后面讲)
用来标识全局变量
using语法 ,可以省略前缀的写法
using namespace 空间名; //就可以省略当前的空间名 //只在当前作用域下有效
空间名的嵌套与空间内容访问
namespace A { int aa=1; namespace B { int bb=1; } } A::B::bb=1001; //剥洋葱 using namespace A::B; bb=1002;
基本输入和输出
#include <iostream>
一般情况都会包含命名空间 using namespace std;
输出: cout<< 做输出
C++支持C语言的格式控制字符
输入: cin>>做输入
#include <iostream>
#include <string>
//using namespace std; 一般都是写在这里,避免std的前缀使用
void testCinCout()
{
using namespace std;
//1.基本变量打印不需要格式控制
cout << "ILoveyou\n";
char str[] = "ILoveyou\n";
cout << str;
int a = 1;
float b = 1.1;
cout << a;
cout << b;
cout << "\n";
//2.如何一次性打印多个数据
cout << a << "\t" << (int)b << "\n";
cout << a << "\t" << int(b) << "\n";
//C语言运算符的隐式转换都是一样的
int aa = 1.111;
//3.C++换行 endl替换\n的作用
cout << "姓名" << endl;
cout << "年龄" << endl;
int iNum = 0;
float fNum = 0.0f; //默认小数是double
//unsigned int uNum = 1u;
//long int lNum = 1L;
cout << "请输入一个整数,一个浮点数,一个字符串:";
//一个一个处理
//cin >> iNum;
//cin >> fNum;
//cin >> str;
//一次处理多个
cin >> iNum >> fNum >> str;
cout << iNum << "\t" << fNum << "\t" << str << endl;
}
//输入的几个问题
//1.字符串输入空格问题
void testStr()
{
using namespace std;
char str[10] = "";
cin >> str; //不带空格的输入
cout << str;
//fflush(stdin) //已被淘汰
//setbuf(stdin,NULL);
while (getchar() != '\n'); //字符和字符串输入前,做了输入
char c;
cin >> c;
cout << c;
//接受空格的输入--->了解
cin.getline(str, 10); //gets_s(str, 10);
cout.write(str, 10);
}
//2.关于命名空间std
void testNamespace()
{
//当不加using namespace std;
std::cout << "没有写using namespace std" << std::endl;
std::string str = "ILoveyou";
}
int main()
{
testStr();
return 0;
}
新数据类型
bool类型
占用内存是一个字节
计算机非零表示成立,只有0或者指针空表示不成立
一般充当函数返回值,或者开关
正常输出是0和1
C++专有的赋值方式,false和true
指针的NULL C++种改为 nullptr
引用类型
理解为起别名
基本引用
类型名& 别名=要起别名的东西; int a=1; int& b=a; //a有另一个名字叫b ,a就是b,b就是a
常引用(右值引用)
类型名&& 别名=右值 int&& a=1; //1就是a ,a就是1
引用一般用在那些地方
函数参数(防止拷贝产生)
函数返回值(增加左值使用)
不能返回局部变量引用
#include <iostream> using namespace std; void testBool() { bool bNum = 1234; cout << bNum << endl; bNum = false; bNum = true; cout << bNum << endl; //boolalpha 用true和false方式打印bool类型 cout << boolalpha << bNum << endl; } void testnullptr() { int* p = nullptr; //NULL char* pc = NULL; } void printStr(const char* str) { cout << str << endl; } void testQuote() { //1.什么是起别名 int 女朋友 = 1; //类型 & 别名=要起别名的东西 int& 小可爱 = 女朋友; //小可爱就是女朋友的别名 小可爱 = 777; cout << 女朋友 << endl; //2.C++种常量要求更严格 //想要变量和常量,就必须用const修饰 printStr("ILoveyou"); //形参必须要有const char str[] = "ILoveyou"; printStr(str); //3.常引用 //int& x = 1; //直接报错,C++对const要求更严格 int aa = 1; const int& x = 1; //第一种写法:const修饰 const int& x2 = aa; //右值引用,只能给右值起别名 int&& xx = 1; //常量是右值(运算符的右边) //int&& xx2 = aa; //右值引用只能给右值起别名 } void modifyA(int a) { a = 1001; } void modifyB(int& a) //int& a=实参; { a = 1001; } void swap(int& a, int& b) { int temp = a; a = b; b = temp; } int g_num = 1001; void modifyPointA(int* p) { p = &g_num; } void modifyPointB(int*& p) //int*& p=实参 { p = &g_num; } //右值引用 void printRightValue(int&& a) { a += 11; //可以增加一个可以修改的功能 cout << a << endl; } void printRight(const int& a) { //a += 11; //常饮用不能修改 cout << a << endl; } int getData() { return g_num; } int& getValue() { return g_num; } void useQuote() { int aa = 1; modifyA(aa); //拷贝本 cout << aa << endl; modifyB(aa); //任何传参都是赋值的方式传参 cout << aa << endl; int bb = 22; swap(aa, bb); cout << aa << "\t" << bb << endl; int pa = 1008; int* p = &pa; cout << "当作函数参数" << endl; modifyPointA(p); cout << *p << endl; modifyPointB(p); cout << *p << endl; printRightValue(11); printRight(11); //返回值就是一个值,这个值只能是右值 //getData() = 1001; 错误 getValue() = 0; //返回引用表示返回变量本身 cout << g_num << endl; } int main() { //testBool(); testQuote(); useQuote(); return 0; }自动推断类型auto类型: 必须根据赋值的数据推断类型,不能直接推断
函数思想
内敛思想 inline关键字
什么样的函数可以成为inline,短小精悍
在结构体中或者类种实现的函数默认内敛(知道即可)
函数重载: C++允许同名不同参数函数存在
参数数目不同
参数类型不同
参数顺序不同(一定建立在不同类型的基础上)
函数缺省: C++中允许给函数形参初始化
缺省顺序 必须从右往左缺省,缺省的参数中间不能存在没有缺省的
没有参入参数,使用的是默认值
#include <iostream>
using namespace std;
inline int Max(int a, int b)
{
return a > b ? a : b;
}
void print(int a)
{
cout << a << endl;
}
void print(int a, int b)
{
cout << a + b << endl;
}
//和上面不是顺序不同
//void print(int b, int a)
//{
// cout << a + b << endl;
//}
void print(int a, char b)
{
cout << "int,char" << endl;
cout << a + b << endl;
}
void print(char a, int b)
{
cout << a + b << endl;
}
//函数缺省
void printData(int a=1, int b=2, int c=3, int d=4)
{
cout << a + b + c + d << endl;
}
int main()
{
print(1, 'A'); //重载调用,优先调用类型一致
//老师的理解: 缺省其实是重载的一种综合写法
printData(); //a=1 b=2 c=3 d=4
printData(10); //a=10 b=2 c=3 d=4
printData(10, 20); //a=10 b=20 c=3 d=4
printData(10, 20, 30); //a=10 b=20 c=30 d=4
printData(10, 20, 30, 40);//a=10 b=20 c=3 d=40
return 0;
}
作业
注册一个CSDN ,把今天学的内容,总结好,分析好,发布一篇博客即可,发完连接发给即可
答疑环节
auto与C语言auto区别
C++中淘汰了C语言用法,只有自动推断用法
void text(float b=0.0,int a=0,char c='a') 然后传的时候,只传int类型和char类型,它给哪个形参传参。。
博客切入代码格式
C++与C的区别(二)
结构体区别
类型上不再需要struct关键字,直接用结构体名即可
C++结构体中允许函数存在
在结构体中声明,在结构体外实现,当然可以直接在结构体中实现
结构体中函数访问数据,是可以直接访问
学会调用,和数据成员方式时一样的
对象(结构体变量).成员
对象指针->成员
(*对象指针).成员
C++在没有写构造函数和权限限定的时候,用法和C语言的用法是一样
#include <iostream>
#include <string>
using namespace std;
struct MM
{
//protected: 不需要深究后续会讲
//MM() {} 不需要深究后续会讲
//属性,特征
//数据成员
char name[20];
int age;
//.....
//行为(方法)
//成员函数
void print()
{
cout << name << "\t" << age << endl;
}
void printData(); //在结构体中声明,在外面实现
//通过外部函数修改数据
int& getAge()
{
return age;
}
};
//结构体名限定,就是告诉别人这个函数来自哪里
void MM::printData()
{
cout << name << "\t" << age << endl;
}
//结构体中的变量必须要通过结构体变量(结构体指针)访问
//C++结构体中的函数访问属性,可以直接访问
int main()
{
struct MM girl = { "小芳",28 };
MM mm = {"小丽",24};
girl.print();
(&mm)->printData();
MM* p = &mm;
p->printData();
p->getAge() = 84;
p->printData();
p->age = 1991;
p->printData();
MM array[3];
return 0;
}
动态内存申请
C语言的动态内存申请
malloc 不带初始化 ,calloc 带初始化,realloc 重新申请
free 释放
C++的动态申请
new(申请)和delete(释放)
单个变量内存申请
数组的动态申请
结构体内存申请
#include <iostream>
#include <cstring>
using namespace std;
void testOneMemory()
{
//申请不做初始化
int* pInt = new int;
*pInt = 123;
cout << *pInt << endl;
char* pChar = new char;
*pChar = 'A';
cout << *pChar << endl;
//申请内存做初始化 ()给单个数据初始化
int* pNum = new int(134);
cout << *pNum << endl;
delete pInt;
pInt = nullptr;
pInt = new int;
*pInt = 332;
cout << *pInt << endl;
delete pInt;
pInt = nullptr;
delete pChar;
pChar = nullptr;
delete pNum;
pNum = nullptr;
}
void testArrayMemory()
{
//一维数组
//1.不带初始化
//长度可以是变量,只要值就可以
int* pInt = new int[3]; //等效产生了int pInt[3]的数组
//const char* pstr = new char[15]; //你 --->大老婆
//const char* pstr1 = pstr; //朋友--->大老婆
//pstr = "ILoveyou"; //你-->二老婆
char* pstr = new char[15];
strcpy_s(pstr, 15, "ILoveyou");
cout << pstr << endl;
//cout << pstr1 << endl;
//带初始化的 一堆数据用 {}
int* pNum = new int[3]{ 1,2,3 };
for (int i = 0; i < 3; i++)
{
cout << pNum[i] << " ";
}
cout << endl;
delete[] pNum;
char* str = new char[20]{ 'A','B','\0' };
cout << str << endl;
delete[] str;
str = nullptr;
str = new char[20]{ "ILoveyou" };
cout << str << endl;
delete[] str;
str = nullptr;
delete [] pInt; //数组的释放 不需要大小
//释放只有两种形式 delete 指针 delete [] 指针
//delete [][] p 没有这种写法
pInt = nullptr;
}
struct MM
{
char* name;
int age;
//成员函数
void printMM()
{
cout << name << "\t" << age << endl;
}
};
void testStructMemory()
{
//new一个对象
int* p = new int(23);
//结构体只能用大括号
MM* pMM = new MM;
//结构体中指针,要做二次申请,才能strcpy,或者赋值
pMM->name = new char[20];
strcpy_s(pMM->name,20, "丽丝");
pMM->age = 188;
pMM->printMM();
//申请顺序和释放顺序是相反
delete[] pMM->name;
delete pMM;
}
int main()
{
//testOneMemory();
//testArrayMemory();
testStructMemory();
return 0;
}
内存池
允许大家申请一段内存,共给程序使用,综合管理内存
#include <iostream>
using namespace std;
//允许大家申请一段内存,共给程序使用,综合管理内存
//malloc 内存是在堆区
//new 内存是自由存储区
void testMemory()
{
char* memorySum = new char[1024];
//.......事情的处理,需要内存,所有内存源自于memorySum
//int* pNum = new(申请内存的开始位置) int[3]
int* pNum = new(memorySum) int[3]{ 1,2,3 };
//char* pstr = new(pNum + 3) char[20]{ "ILoveyou" };
//和下面这句话是等效的
char* pstr = new(memorySum + 12) char[20]{ "ILoveyou" };
for (int i = 0; i < 3; i++)
{
cout << pNum[i] << " ";
}
cout << endl;
for (int i = 0; i < 3; i++)
{
cout << ((int *)memorySum)[i] << " ";
}
cout << endl << pstr << endl;
cout << (memorySum + 12) << endl;
delete[] memorySum;
memorySum = nullptr;
}
int main()
{
testMemory();
return 0;
}
string类型
只需要知道有这种用法即可,不需要大家深究为什么,因为string本身是一个类,需要讲完类的大部分知识,才能追究为什么这样做。自己也可以封装一个string 类型
string创建
带初始化
不带初始化
通过另一个字符串创建
string基本操作
拷贝
赋值
连接
比较
C++string与C语言string.h
string 其他函数操作
#include <string> //注意和string.h区别
#include <iostream>
#include <cstring> //string.h和cstring是一样
#include <stdio.h>
using namespace std;
void createString()
{
//std::string str;
string str1;
str1 = "ILoveyou"; //所以一般用string不会加const
cout << "First:" << str1 << endl;
const string cstr;
//cstr = "IMissyou"; 错误,常属性不能修改
string str2("ILoveyou");
cout << str2 << endl;
string str3 = "IMissyou"; //喜欢这种方式
cout << str3 << endl;
string str4(str3);
cout << str4 << endl;
string str5 = str4;
cout << str5 << endl;
//一般没有长度限定,在你使用范围下
string str = "2333333333333333333333333333333333333333333333333333333333333";
}
void operatorString()
{
string str1 = "one";
string str2 = "two";
string str3 = str2;
cout << str3 << endl;
//没有减法
string str4 = str1 + str2;
//等效: string str4=str1.append(str2);
//C++中尽量用string 不要用char* ,可以用
//比较直接比较即可
//> < != ==
//str1.compare(str2) 0 -1 1
if (str1 > str2) //比较依旧按照char* 去比较
{
cout <<"大的 "<< str1 << endl;
}
else
{
cout << "大的 " << str2 << endl;
}
}
void compareCAndCpp()
{
//C++中是一个自定义类型(类),目前当作结构体即可
//C++string 不能用到C语言的字符串处理函数
//C++如何转换为C语言的char*
//c_str() data()函数
string str1 = "ILoveyou";
//printf("%s", str1);
printf("%s\n", str1.c_str());
printf("%s\n", str1.data());
//outtextxy(int x,int y,char* str);
//直接把数字转换为相应的字符串
string str2 = to_string(1234);
//atoi
cout << str2 << endl;
}
void exOperator()
{
//采用下表法打印string
string str = "IMissyou";
//C++string中没有记录\0
for (int i = 0; i < 8; i++)
{
cout << str[i];
}
cout << endl;
//其他函数操作
//万金油函数
//empty()
//size();
string mystring = "IMissyou";
//cout << sizeof(mystring) << endl; //28
//cout <<"容量:" <<mystring.capacity() << endl;
cout <<"mystring:"<< mystring.size() << endl;
string strEmpty;
if (strEmpty.empty()) //return length==0;
{
cout << "string为空" << endl;
}
}
void initString()
{
char* str = new char[15];
//做了自动扩增处理
}
int main()
{
//createString();
//operatorString();
//compareCAndCpp();
exOperator();
return 0;
}
作业
编程题:
二维数组的动态内存申请,采用子函数的方式 为二级指针申请内存,和释放内存
把今天学的内容,总结好,分析好,加上编程题,发布一篇博客即可,发完连接发给即可
答疑环节
结构体内 char * 为啥下面复制字符串的时候要用 const
这个跟赋值的值有关系,因为C++对const要求更严格,=两边类型必须一致
所以c语言申请内存空间calloc,realloc可以讲一下吗
#include <string>
#include <iostream>
#include <stdio.h>
using namespace std;
void testConst()
{
//指针变量=常量地址
const char* str = "ILoveyou";
char str2[] = "ILoveyou";
char* str3 = str2;
}
void print(int array[], int arrayNum)
{
for (int i = 0; i < arrayNum; i++)
{
cout << array[i] << " ";
}
cout << endl;
}
void testCalloc()
{
//int pMnum[3]
//申请内存不做初始化
int* pMNum = (int*)malloc(sizeof(int) * 3);
if (pMNum == nullptr)
return;
memset(pMNum, 0, sizeof(int) * 3); //内存初始化
print(pMNum, 3);
int* pCNum = (int*)calloc(3, sizeof(int));
print(pCNum, 3);
int* pint = (int*)malloc(sizeof(int));
if (pint == NULL)
{
return;
}
*pint = 1999;
pint = (int *)realloc(pint, sizeof(int) * 3);
pint[1] = 123;
pint[2] = 134;
print(pint, 3);
free(pint);
}
int main()
{
testCalloc();
return 0;
}
auto p=new int[3] 没问题
内存池代码理解
作业指导C语言版本
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int** createArray2D(int row, int cols)
{
//int *pArray=(int*)malloc(sizeof(int)*3)
int** pArray = (int**)malloc(sizeof(int*) * row);
if (pArray == NULL)
return NULL;
for (int i = 0; i < row; i++)
{
//一级指针
pArray[i] = (int*)malloc(sizeof(int) * cols);
}
return pArray;
}
//有兴趣的可以用传参的方式
void mallocArray2D(int*** pArray, int row, int cols)
{
}
int main()
{
int** p = createArray2D(4, 3);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
p[i][j] = i * j;
printf("%d\t", p[i][j]);
}
printf("\n");
}
return 0;
}
C++类和对象
类和对象的基本概念
什么是类,一系列事物的抽象,万物皆可为类
类是有两部分组成: 属性 行为
属性: 事物的特征--->数据类型描述
行为: 事物的操作--->函数描述
什么是对象: 类的具体化,类的实例化.
类的特点: 封装,继承/派生,多态
类的定义
创建语法
class 类名
{
//权限限定词
public:
protected:
private:
}; //一定有一个;
权限限定 作用
类外只能访问public属性下面的 东西,习惯把 public属性 叫做类外的接口
类外访问 类中的数据,只能通过对象访问,当然static成员除外
protected和private 类外都不可以访问 ,但是可以提供共有接口间接访问
默认属性(没有写在权限限定词下的属性)是 私有属性
权限限定词,只是用来限定类外的访问,并不是限定中的访问
protected和private 有区别 ,继承有 区别,对类外 都是不可以访问
C++结构体 在一定程序可以直接 当作是类
默认属性是公有属性
#include <iostream>
#include <string>
using namespace std;
class GirlFriend
{
void print()
{
cout << "不在限定词下的属性" << endl;
cout << "默认为私有属性" << endl;
}
public:
//共有属性
//成员函数
//类中实现函数
void printData()
{
cout << m_name << "\t" << m_age << endl;
}
//为了访问不能访问的部分,通常提供一些接口
void initData(string name, int age);
protected:
//保护属性
//数据成员
string m_name;
private:
//当前类不做继承处理,数据成员写成私有属性
int m_age;
};
//类外实现类中函数,需要类名限定,告诉别人这个函数是哪里来的
void GirlFriend::initData(string name,int age)
{
//Lisa.initData("Lisa", 19); name="Lisa" age=19
m_name = name; //Lisa.m_name=Lisa
m_age = age; //Lisa.m_age=19;
//mm.initData("MM", 29); name="MM" age=29
//mm.m_name=MM;
//mm.age=29
}
struct MM
{
int num; //默认属性是公有属性
protected:
string name;
private:
int age;
};
void testMM()
{
//MM mm = { 1001,"name",28 };
MM mm;
mm.num = 103;
//mm.name = "Ilove";
//mm.age = 13;
}
int main()
{
GirlFriend Lisa;
Lisa.initData("Lisa", 19);
Lisa.printData();
//类外只能访问public
//Lisa.m_name = "Lisa";
//Lisa.m_age = 18;
GirlFriend mm;
mm.initData("MM", 29);
mm.printData();
//mm.print(); --->不能访问私有属性
return 0;
}
对象创建
普通对象
对象数组
new 一个对象
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
void print()
{
cout << name << "\t" << age << endl;
}
void initData(string nname,int nage)
{
name = nname;
age = nage;
}
protected:
//新标准,可以在类中给数据直接初始化
string name="默认值";
int age=0;
};
int main()
{
//没有写构造函数的情况下,和C语言的创建方式是一样的
MM mm;
mm.print(); //没有初始化数据
MM mmArray[4]; //一般很少用对象数组
//mmArray[0]----mmArray[3]
//数组: 多个变量名有规律,内存连续的变量的集合
for (int i = 0; i < 4; i++)
{
mmArray[i].initData(string("name") + to_string(i), i + 19);
mmArray[i].print();
}
MM* p = new MM;
p->initData("张三", 18);
p->print();
delete p;
p = nullptr;
return 0;
}
成员访问(初始化)
通过提供 公有接口传参的方式初始化数据
通过提供 公有接口返回值的方式初始化数据
默认初始化
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
//传参
void initData(string name, int age)
{
m_name = name;
m_age = age;
}
//返回引用
string& getName()
{
return m_name;
}
int& getAge()
{
return m_age;
}
void print()
{
cout << m_name << "\t" << m_age << endl;
}
protected:
//默认初始化
string m_name="默认值";
int m_age=0;
//不做初始化是一个垃圾值
};
int main()
{
MM girl;
girl.initData("girl", 19);
girl.print();
MM mm;
mm.getName() = "mm";
mm.getAge() = 18;
mm.print();
MM boy;
boy.print();
return 0;
}
面向对象的编程方式
#include <iostream>
#include <string>
using namespace std;
#if 0
struct Node
{
int data;
struct Node* next;
};
struct Node* createList()
{
Node* headNode = new Node;
headNode->next = nullptr;
return headNode;
}
struct Node* createNode(int data)
{
Node* newNode = new Node;
newNode->data = data;
newNode->next = nullptr;
return newNode;
}
void insertData(Node* headNode, int data)
{
Node* newNode = createNode(data);
newNode->next = headNode->next;
headNode->next = newNode;
}
void printList(Node* headNode)
{
Node* pMove = headNode->next;
while (pMove != nullptr)
{
cout << pMove->data<<" ";
pMove = pMove->next;
}
cout << endl;
}
void testListC()
{
Node* list = createList();
insertData(list, 10);
insertData(list, 20);
printList(list);
}
#endif
#if 0
struct Node
{
int data;
Node* next;
};
class List
{
public:
void createList()
{
headNode = new Node;
headNode->next = nullptr;
}
void insertData(int data)
{
Node* newNode = new Node;
newNode->data = data;
newNode->next = nullptr;
newNode->next = headNode->next;
headNode->next = newNode;
}
void printList()
{
Node* pMove = headNode->next;
while (pMove != nullptr)
{
cout << pMove->data << " ";
pMove = pMove->next;
}
cout << endl;
}
protected:
Node* headNode; //用一个指针表示整个表头
};
void testList1()
{
List* pList = new List; //C++第一步:创建对象
pList->insertData(10);
pList->insertData(20);
pList->printList();
}
#endif
class Node
{
public:
Node*& getNext()
{
return next;
}
int& getData()
{
return data;
}
protected:
int data;
Node* next;
};
class List
{
public:
void createList()
{
headNode = new Node;
headNode->getNext() = nullptr;
}
void insertData(int data)
{
Node* newNode = new Node;
newNode->getNext() = nullptr;
newNode->getData() = data;
newNode->getNext() = headNode->getNext();
headNode->getNext() = newNode;
}
void printList()
{
Node* pMove = headNode->getNext();
while (pMove != nullptr)
{
cout << pMove->getData() << "\t";
pMove = pMove->getNext();
}
cout << endl;
}
protected:
Node* headNode;
};
void testList2()
{
List* pList = new List; //C++第一步:创建对象
pList->createList();
pList->insertData(10);
pList->insertData(20);
pList->printList();
}
int main()
{
//testListC();
testList2();
return 0;
}
上节课作业
#include <iostream>
using namespace std;
void createArray2D(int**& p, int row, int cols)
{
p = new int* [row];
for (int i = 0; i < row; i++)
{
p[i] = new int[cols];
}
}
void deletePoint(int**& p,int row)
{
for (int i = 0; i < row; i++)
{
delete [] p[i];
}
delete[] p;
}
void testArray(int** p, int row, int cols)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < cols; j++)
{
p[i][j] = i * j;
cout << p[i][j] << "\t";
}
cout << endl;
}
}
void modify(int a)
{
a = 1001;
}
//C语言
//修改(0级)普通变量 传入一级指针(普通变量地址)
//修改该一级指针,传入二级指针
//修改该二级指针,传入三级指针
void modify2(int& a) //int& a=实参
{
a = 1001;
}
void createArray(int*** p, int row, int cols)
{
*p = new int* [row];
for (int i = 0; i < row; i++)
{
(*p)[i] = new int[cols];
}
}
int main()
{
int a = 1;
modify(a);
cout << a << endl;
modify2(a);
cout << a << endl;
int** p = nullptr;
createArray2D(p, 3, 4);
testArray(p, 3, 4);
deletePoint(p, 3);
int** array = nullptr;
createArray(&array, 3, 4);
testArray(array, 3, 4);
deletePoint(array, 3);
return 0;
}
本节课作业
自己整理笔记,带观点,和代码,代码用来验证观点
答疑环节
数据放在protected是为了后续的友元访问吗
是为了 在继承 当作子 类的访问父类属性,父类的私有 属性子类不能访问
就是string 类型没有\0
后面 自己可以尝试写 string 知道 原理,目前只需要知道 string记算长度 不算\0
申请内存应该是为了合理分配内存吧用完就释放了
合理使用内存,用户需要多少内存,可以让用户决定,避免内存浪费
保存数据数据的效果
链表用--->容器 ,用来装数据的 类似一个内存不是连续的数组,
怎么选择容器,方便操作解决问题容器容易度选择
C++构造和析构
构造函数
构造函数长什么样子
函数名和类名相同
没有返回值
如果不写构造函数,任何类中都存在一个默认的构造函数
默认的构造函数是无参的。
当我们自己写了构造函数,默认的构造函数就不存在
构造函数在构造对象的时候调用
delete可以用来删掉默认的函数
指定使用默认的无参构造函数,用default说明
允许构造函数调用另一个构造函数,只是要用初始化参数列表的写法
初始化参数列表 : 只有构造函数有
构造函数名(参数1,参数2,...):成员1(参数1),成员2(参数2),...{}
避免形参名和数据成员名相同的导致问题
构造函数干嘛的
构造函数用来构造对象
构造函数更多是用来初始化数据成员
思考题?
为什么不写构造函数可以构造对象? 是因为存在一个默认的无参构造函数,所以可以构造无参对象
构造函数重载为了什么? 为了构造不同长相的对象。
#include <iostream>
using namespace std;
class MM
{
public:
//MM() = delete; 删掉默认的构造函数
MM(string mmName, int mmAge)
{
name = mmName;
age = mmAge;
cout << "带参构造函数" << endl;
}
//MM()
//{
// cout << "无参构造函数" << endl;
//}
MM() = default; //使用的是默认无参构造函数
void print()
{
cout << name << " " << age << endl;
}
protected:
string name="Lisa";
int age=18;
};
//为了能够构造不同长相的对象,我们会给构造函数缺省处理
class Boy
{
public:
//Boy(string mname="", int mage=19)
//{
// name = mname;
// age = mage;
//}
//上面函数 等效可以实现下面三个函数的功能
Boy() {}
Boy(string mName) { name = mName; }
//出错:没有与之匹配的构造函数
Boy(string mName, int mage) { name = mName; age = mage; }
protected:
string name;
int age;
};
//初始化参数列表的写法
string girlName = "Baby";
class Student
{
public:
Student(string mname="", int mage=18) :name(mname), age(mage)
{
cout << "初始化参数列表" << endl;
//继承和类的组合必须采用初始化参数列表写法
}
Student(int mage) :name(girlName), age(mage) {}
protected:
string name;
int age;
};
//构造函数可以调用另一个构造函数初始化数据
class TT
{
public:
TT(string name, int age) :name(name), age(age) {}
//委托构造:允许构造函数调用另一个构造函数
TT():TT("默认",18) {} //没有给数据初始化
void print()
{
cout << name << "\t" << age << endl;
}
protected:
string name;
int age;
};
int main()
{
//MM mm; 构造无参的对象,需要无参构造函数
MM mm("mm", 28);
mm.print();
MM girl;
girl.print();
Boy boy1;
Boy boy2("流浪之子");
Boy boy3("王子", 18);
TT tt;
tt.print();
return 0;
}
析构函数
析构函数长什么样子?
无返回值
无参数
函数名: ~类名
不写的话会存在默认的析构函数
析构函数不需要自己 调用,对象死亡的之前会调用析构函数
析构函数用来干嘛?(什么时候需要自己手动写析构函数)
当类中的数据成员是指针,并且动态申请内存就需要手写析构
析构函数用来释放数据成员申请动态内存
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MM
{
public:
MM(const char* pstr, int age) :age(age)
{
str = new char[strlen(pstr) + 1];
strcpy_s(str,strlen(pstr)+1, pstr);
}
void print()
{
cout << str << "\t" << age << endl;
}
//void freeMemory()
//{
// delete[] str;
//}
~MM();
protected:
char* str;
int age;
};
MM::~MM()
{
cout << "我叫做析构函数" << endl;
delete[] str;
}
int main()
{
{
MM mm("张三", 18);
//mm.~MM(); //手动调用会导致二次释放问题
mm.print();
//mm.freeMemory();
//mm.print();
}
cout << "主函数" << endl;
//new一个对象的时候,只有delete 才会调用析构函数
{
MM* pObject = new MM("baby", 12);
delete pObject;
pObject = nullptr;
//delete [] p ----- delete p
}
return 0;
}
拷贝构造函数
拷贝构造函数也是构造函数,长相和构造函数一样的,只是参数是固定
拷贝构造函数唯一的参数是对对象引用
不写拷贝构造函数,也存在一个默认的拷贝构造函数
拷贝构造函数作用: 通过一个对象去初始化另一个对象
问题?
什么时候调用拷贝构造?
当通过一个对象去创建出来另一个新的对象时候需要调用拷贝
拷贝构造什么时候需要加const修饰参数?
当存在匿名对象赋值操作的时候,必须要const修饰
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
MM() = default;
MM(string name, int age) :name(name), age(age) {}
void print()
{
cout << name << "\t" << age << endl;
}
//拷贝构造
MM(const MM& mm) //MM girl(mm);
{
name = mm.name; //girl.name=mm.name
age = mm.age; //girl.age=mm.age
cout << "拷贝构造" << endl;
}
protected:
string name;
int age;
};
void printData(MM mm) //MM mm=实参;
{
mm.print();
}
void printData2(MM& mm) //不存在拷贝本
{
mm.print();
}
int main()
{
MM mm("mm", 18);
mm.print();
//显示调用调用
cout << "显示调用调用" << endl;
MM girl(mm); //通过一个对象创建另一个对象
girl.print();
//隐式调用
cout << "隐式调用" << endl;
MM girl2 = mm; //拷贝构造
girl2.print();
MM girl3;
girl3 = mm; //运算符重载
girl3.print();
//函数传参
cout << "第一种调用形态" << endl;
printData(mm);
cout << "第二种调用形态" << endl;
printData2(mm);
//无名对象 匿名对象
MM temp;
temp = MM("匿名", 18);
temp.print();
//匿名对象创建对象时候,拷贝构造一定要用const修饰
MM temp2 = MM("匿名", 199);
return 0;
}
深浅拷贝
浅拷贝: 默认的拷贝构造叫做浅拷贝
深拷贝: 拷贝构造函数中做了new内存操作,并且做拷贝赋值的操作
#include<iostream>
#include <cstring>
#include <string>
using namespace std;
class MM
{
public:
MM(const char* mname, int age) :age(age)
{
name = new char[strlen(mname) + 1];
strcpy_s(name, strlen(mname) + 1, mname);
}
void print()
{
cout << name << "\t" << age << endl;
}
MM(const MM& object)
{
//name = object.name;
name = new char[strlen(object.name) + 1];
strcpy_s(name, strlen(object.name) + 1, object.name);
//name = object.name;
age = object.age;
}
~MM()
{
delete[] name;
}
protected:
char* name;
int age;
};
int main()
{
{
MM mm("baby", 19);
MM girl(mm);
MM gm = mm;
mm.print();
girl.print();
gm.print();
}
return 0;
}
构造和析构顺序问题
普通对象,构造顺序和析构顺序是相反
new出来的对象,delete会直接调用析构函数
static对象,当程序关闭的时候,生命周期才结束,所以是最后释放
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
MM(string name="x") :name(name) {
cout << name;
}
~MM(){
cout << name;
}
protected:
string name;
};
int main()
{
{
MM mm1("A"); //A
static MM mm2("B"); //B 程序关闭时候才死亡,最后析构
MM* p3 = new MM("C"); //C
MM mm4[4]; //xxxx
delete p3; //C delete 直接调用析构
p3 = nullptr;
//xxxxAB
}
//ABCxxxxCxxxxAB
return 0;
}
C++结构体
#include <iostream>
#include <string>
using namespace std;
struct MM
{
//默认为公有属性
//类中默认属性是私有属性
//protected:
string name;
int age;
public:
MM(string name) :name(name)
{
cout << "构造函数" << endl;
}
MM(const MM& object)
{
name = object.name;
age = object.age;
cout << "拷贝构造" << endl;
}
~MM()
{
}
};
int main()
{
//采用创建时候赋值的方式,也是调用构造函数
//MM object = { "lisa",19 }; 错误,因为没有两个参数的构造函数
MM object = { "lisa" };
cout << object.name << "\t" << object.age << endl;
//C++结构体一旦写了构造函数,就必须按照C++类的方式的去用
MM mm(object);
cout << mm.name << "\t" << mm.age << endl;
return 0;
}
本节课作业
1.自己整理笔记,带观点,和代码,代码用来验证观点
2.在博客加上一个代码:(简单写个自己的myString类,实现以下string功能)
//1.实现string中创建方式
string str1;
string str2("ILoveyou");
string str3(str1);
string str4 = str2;
//2.通过实现data和c_str函数 打印字符串
cout << str2.c_str() << endl; //打印ILoveyou
cout << str2.data() << endl; //打印ILoveyou
//3.实现append 实现字符串的链接
string strOne="one";
string strTwo="two";
string strThree=strOne.append(strTwo);
cout<<strThree.data()<<endl; //onetwo
//4.实现字符串比较
cout<<strOne.compare(strOne)<<endl; //0
//5.手写析构函数释放内存
答疑时间
为什么要手动写析构函数? 因为默认的不会释放数据成员动态申请的内存
函数名和类型相同函数叫做构造函数
函数名字是~类名的无参函数叫做析构函数
以对象的引用为参数的构造函数叫做拷贝构造函数(复制构造函数)
怎莫写出来,默认的构造函数,就是那种在没有传参的时候,的那一串垃圾值
class Boy
{
public:
Boy() {}
void print()
{
cout << a << "\t" << b << "\t" << c << endl;
}
protected:
int a;
int b;
int c;
};
int main()
{
return 0;
}
C++特殊成员
const成员
const数据成员
const类型变量是不可以修改,只读模式
必须采用初始化参数列表方式进行初始化
const成员函数
写法上, const写在函数后面
常成员函数是不能够修改数据成员,只读数据成员
常成员函数可以与普通函数同时存在
普通函数和常成员函数相同时,普通对象优先调用普通函数
普通对象可以调用常成员函数
const对象: const修饰的对象
常对象只能调用常成员函数
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
MM(string name, int num) :num(num)
{
MM::name = name; //可以用,也可以不用初始化列表
//MM::num = 1001; 必须要用初始化参数列表方式初始化
}
void print()
{
//不能修改
//num = 1001; 错误,只读模式
cout << name << " " << num << endl;
}
//常成员函数
void print()const
{
//name = "修改"; 错误,常成员函数不能修改数据
//num = 199;
cout << "常成员函数" << endl;
}
void printData()
{
cout << "普通函数" << endl;
}
protected:
string name;
const int num; //const数据成员
};
int main()
{
MM mm("对象", 18);
mm.print(); //普通对象调用普通函数
const MM cmm("常对象", 20);
cmm.print(); //常对象调用常成员函数
//cmm.printData(); //错误!,常对象只能调用普通函数
return 0;
}
static成员
static成员是不属于对象,是属于类的,意味着是所有对象共有的,调用可以不需要对象,当然你可以用对象调用
static成员依旧受权限限定。
static数据成员
必须在类外初始化,不再需要static修饰,但是需要类名限定
类中初始化是错误的,不能采用初始化参数列表方式初始化
static成员函数
static写在函数前面即可
调用非静态成员 必须要指定对象
static对象
释放是最后释放的
#include <iostream>
using namespace std;
class MM
{
public:
MM(string name=""):name(name)
{
num++;
}
static void printMM();
static void printData(MM& mm)
{
cout << mm.name <<" "<<num <<endl;
}
protected:
string name;
public:
static int num;
};
//类外初始化,不再需要static修饰,但是需要类名限定
int MM::num = 1;
//类外初始化,不再需要static修饰
void MM::printMM()
{
//调用非静态数据成员,必须要指定对象
//cout << name << endl; 当这个函数不采用对象去调用,name没有来源
//静态调用静态,没什么要求
cout << num << endl;
cout << "静态成员函数" << endl;
}
int main()
{
//静态数据成员访问,可以不需要对象
cout << MM::num << endl;
//什么叫做共有的
MM mm("mm");
//静态数据成员可以通过对象去访问
cout << mm.num << endl; //此时num等于2
MM array[3]; //5
MM* p = new MM("newMM"); //6
cout << MM::num << endl;
cout << p->num << endl;
cout << mm.num << endl;
delete p;
p = nullptr;
//静态成员函数
MM::printMM();
mm.printMM();
MM::printData(mm);
return 0;
}
友元
友元? friend描述的关系。友元只是提供一个场所,赋予对象具有打破类的权限定(无视权限)
友元函数
普通友元函数
以另一个类的成员函数充当友元函数,顺序如下:
B 类
A类
A类的友元函数(B类的成员函数)
友元类
#include <iostream>
using namespace std;
class MM
{
friend class GG;
public:
MM(string name, int age) :name(name), age(age) {}
protected:
string name;
int age;
};
class GG
{
public:
void print()
{
MM mm("mm", 18);
cout << mm.name << "\t" << mm.age << endl;
}
void printMM(MM& mm)
{
cout << mm.name << "\t" << mm.age << endl;
}
MM& returnMM(MM& mm)
{
return mm;
}
protected:
};
//互为友元类的写法
class A
{
friend class B;
public:
void printData();
protected:
string data="A";
};
class B
{
public:
friend class A;
void printData()
{
A a;
cout << a.data << endl;
}
protected:
string data = "B";
};
void A::printData()
{
B b;
cout << b.data << endl;
}
int main()
{
MM mm("mm", 18);
GG gg;
gg.print();
gg.printMM(mm);
//cout << gg.returnMM(mm).name << endl; 错误,出了友元类,没有权限
//互为友元
B b;
b.printData();
A a;
a.printData();
return 0;
}
this指针与explicit
explicit修饰构造函数使用,不让隐式转换构造
this指针
避免形参名和数据成员同名,通指对象的地址
充当函数返回值,返回对象自身,用*this表示对象本身
静态成员函数中是不能使用this指针
#include <iostream>
using namespace std;
class MM
{
public:
explicit MM(int age) :age(age) {}
void print()
{
cout << age << endl;
}
protected:
int age;
};
class GG
{
public:
GG(string name, int age) :name(name), age(age) {}
//普通函数不存在初始化参数列表
void initData(string name, int age)
{
//类名限定 帮助计算机去识别
GG::name = name;
this->age = age;
}
void print()
{
cout << this->name << " " << this->age << endl;
}
void printThis()
{
cout << this << endl;
}
GG& returnGG()
{
return *this;
}
void printGG2(GG& gg) {}
static void printStatic()
{
GG gg("this", 19);
cout << gg.name << "\t" << gg.age << endl;
}
protected:
string name;
int age;
};
int main()
{
//explicit 不让隐式转换构造
//MM mm = 12;
//MM temp = 1.33;
MM temp(12);
temp.print();
GG gg("长沙吴彦祖", 28);
gg.print();
gg.initData("顿开吴彦祖", 38);
gg.print();
cout << &gg << endl;
gg.printThis();
GG boy("哥哥吴彦祖", 38);
cout << &boy << endl;
boy.printThis();
gg.returnGG().returnGG().returnGG().returnGG().returnGG().returnGG().print();
GG::printStatic();
return 0;
}
上节课作业
#include <iostream>
#include <cstring>
using namespace std;
class mystring
{
public:
//mystring()
//{
// strSize = 1;
// str = new char;
// *str='\0';
//};
mystring(const char* str="")
{
strSize = strlen(str) + 1;
mystring::str = new char[strSize];
strcpy_s(mystring::str,strSize,str);
}
mystring(const mystring& object)
{
strSize = object.strSize;
str = new char[strSize];
strcpy_s(str, strSize, object.str);
}
char* c_str()
{
return str;
}
char* data()
{
return str;
}
mystring append(const mystring& object)
{
mystring temp;
temp.strSize = mystring::strSize + object.strSize-1;
temp.str = new char[temp.strSize];
memset(temp.str, 0, temp.strSize);
strcat_s(temp.str, temp.strSize, str);
strcat_s(temp.str, temp.strSize, object.str);
return temp;
}
int compare(const mystring& object)
{
return strcmp(str, object.str);
}
~mystring()
{
delete[] str;
str = nullptr;
}
protected:
char* str; //需要存储
int strSize;
};
int main()
{
{
//1.实现string中创建方式
mystring str1;
mystring str2("ILoveyou");
mystring str3(str1);
mystring str4 = str2;
//2.通过实现data和c_str函数 打印字符串
cout << str2.c_str() << endl; //打印ILoveyou
cout << str2.data() << endl; //打印ILoveyou
//3.实现append 实现字符串的链接
mystring strOne = "one";
mystring strTwo = "two";
mystring strThree = strOne.append(strTwo);
cout << strThree.data() << endl; //onetwo
//4.实现字符串比较
cout << strOne.compare(strOne) << endl; //0
}
//5.手写析构函数释放内存
return 0;
}
本节课作业
自己整理笔记,带观点,和代码,代码用来验证观点
C++运算符重载
什么是运算符重载
赋予运算符具有操作自定义类型数据功能
友元函数重载运算符
类成员函数重载运算符
#include <iostream>
using namespace std;
/*
什么是运算符重载?
赋予运算符具有操作自定义类型数据功能
运算符重载的实质是什么?
运算符重载的实质本身就是函数调用
运算符重载函数的写法
函数返回值 函数名(函数参数)
函数返回值 :运算完成后的值决定的 Complex
函数名 : operator 加上重载运算符组成函数名 operator+
参数 :看运算符的操作数,具体参数个数是要看你重载函数形式是什么
函数体 : 写运算符具体想要的操作
*/
class Complex
{
public:
Complex(int a=0, int b=0) :a(a), b(b) {}
void print()
{
cout << a << endl;
cout << b << endl;
}
friend Complex operator+ (Complex one, Complex two);
//类成员函数重载,参数个数等于操作减一
bool operator> (Complex object)
{
if (this->a > object.a)
{
return true;
}
else if (this->a == object.a && this->b > object.b)
{
return true;
}
else
{
return false;
}
}
protected:
int a;
int b;
};
//友元重载: 参数个数就是操作数据
Complex operator+ (Complex one, Complex two)
{
return Complex(one.a + two.a, one.b + two.b);
}
int main()
{
Complex one(1, 1);
Complex two(2, 0);
Complex three;
three = one + two; //Complex 重载函数的隐式调用
three.print();
//显式调用
Complex result;
result = operator+(one, two);
if (one > two) //one > two 是bool
{
cout << "one 比较大" << endl;
}
//对象可以表示一个数据,所以参数应该少一个
if (one.operator>(two))
{
cout << "one比较大" << endl;
}
return 0;
}
特殊运算符重载
流运算符重载
cin类型 : istream类的对象
cout类型:ostream类的对象
流运算符 >> <<
必须采用友元函数形式重载
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
MM(string name = "", int age = 18) :name(name), age(age) {}
friend istream& operator>>(istream& in, MM& mm);
friend ostream& operator<<(ostream& out, MM& mm);
protected:
string name;
int age;
};
//其他运算符
//= () -> [] 只能采用类的成员函数形式重载
//流重载采用友元方式
//. .* ?: :: 不能重载
istream& operator>>(istream& in, MM& mm)
{
in >> mm.name >> mm.age;
return in;
}
ostream& operator<<(ostream& out, MM& mm)
{
out << mm.name << "\t" << mm.age << endl;
return out;
}
int main()
{
string str;
cin >> str;
cout << str << endl;
MM mm;
cin >> mm; //void operator>>(istream& in,MM& mm)
cout << mm; //void operator<<(ostream& out,MM& mm)
cin >> str >> mm;
cout << str << endl << mm;
return 0;
}
++ --运算符重载
解决问题:前置和后置的问题:增加无用参数 int去表示当前运算符重载是后置操作
文本重载 (新标准中的,稍微落后一点开发工具不适用)
其他运算符
= () -> [] 只能采用类的成员函数形式重载
流重载采用友元方式
. .* ?: :: 不能重载
对象隐式转换
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
using namespace std;
class MM
{
public:
MM(string name ,int age):name(name),age(age){}
friend ostream& operator<<(ostream& out, MM& object)
{
out << object.name << "\t" << object.age << endl;
return out;
}
MM operator++(int) //需要一个无用参数 充当标记
{
int num = age;
age++;
return MM(name, num);
//上面三行等效下面一行
//return MM(name,age++);
}
MM operator++()
{
return MM(name, ++age);
}
//类的对象的隐式转换 operator
operator int()
{
return age;
}
protected:
string name;
int age;
};
//文本重载
unsigned long long operator"" _h(unsigned long long num)
{
return 60 * 60*num;
}
unsigned long long operator"" _min(unsigned long long num)
{
return 60 * num;
}
int main()
{
MM mm("小芳", 18);
cout << mm << endl;
int num = 1;
int result = num++; //result=1 num=2
cout << result << "\t" << num << endl;
result = ++num; //result=3 num=3
cout << result << "\t" << num << endl;
MM object = mm++;
cout << object<<mm; //age=18 mm: 19
object = ++mm; //age:20 mm: 20
cout << object<<mm;
//this_thread::sleep_for(3s);
cout << "3s结束" << endl;
int second = 1_h;
cout << second << "s" << endl;
int sum = 1_h + 18_min + 30;
cout << sum << "s" << endl;
MM girl("girl", 18);
int girlAge = girl;
cout << girlAge << endl;
return 0;
}
重载综合案例
#include <iostream>
#include <string>
#include <functional>
#include <memory>
using namespace std;
class Int
{
public:
Int(int num) :num(num) {}
int& data()
{
return num;
}
string tostr()
{
return to_string(num);
}
//算术运算符重载
Int operator+(const Int& value)
{
return Int(this->num + value.num);
}
//友元重载 :操作数-1 等于重载函数的参数个数
friend Int operator-(const Int& one, const Int& two)
{
//operator-(one,two);
return Int(one.num - two.num);
}
Int operator+=(const int& value)
{
return Int(this->num + value);
}
Int operator+=(const Int& value)
{
return Int(this->num + value.num);
}
Int operator++(int)
{
return Int(this->num++);
}
Int operator++()
{
return Int(++this->num);
}
Int operator&(const Int& value)
{
return Int(this->num & value.num);
}
bool operator!()
{
return !this->num;
}
Int operator-()
{
return Int(-this->num);
}
friend ostream& operator<<(ostream& out, const Int& object)
{
out << object.num << endl;
return out;
}
friend istream& operator>>(istream& in, Int& object)
{
in >> object.num;
return in;
}
int* operator&()
{
return &this->num;
}
bool operator>(const Int& object)
{
return this->num > object.num;
}
protected:
int num;
};
void print(const int& num)
{
cout << num << endl;
}
//重载[]
class myvector
{
public:
myvector(int size)
{
base = new int[size] {0};
}
int& operator[](int index)
{
return base[index];
}
protected:
int *base;
};
//重载()运算符
class Function
{
typedef void(*PF)();
public:
Function(PF pf) :pf(pf) {}
void operator()()
{
pf();
}
protected:
PF pf;
};
//->
struct MM
{
string name;
int age;
MM(string name, int age) :name(name), age(age) {}
};
class Auto_ptr
{
public:
Auto_ptr(int* ptr) :ptr(ptr) {}
Auto_ptr(MM* ptr) :ptrMM(ptr) {}
int& operator*()
{
return *ptr;
}
MM* operator->()
{
return ptrMM;
}
~Auto_ptr()
{
if (ptrMM)
{
delete ptr;
ptr = nullptr;
}
if (ptrMM)
{
delete ptrMM;
ptrMM = nullptr;
}
}
protected:
int* ptr;
MM* ptrMM;
};
void testAuto_ptr()
{
//int* pInt = new int(18);
//Auto_ptr ptr(pInt);
//上面两行等效下面一行
Auto_ptr ptr(new int(18));
cout << *ptr << endl;
Auto_ptr ptrMM(new MM("mm", 19));
cout << ptrMM->name<< endl;
cout << ptrMM->age << endl;
#if 0
auto_ptr<int> autoPtr(new int(18));
cout << *autoPtr << endl;
auto_ptr<MM> autoPtrMM(new MM("mm", 19));
cout << autoPtrMM->name << endl;
cout << autoPtrMM->age << endl;
#endif
}
void print()
{
cout << "测试函数" << endl;
}
int max(int a, int b)
{
return a + b;
}
int main()
{
print(1);
int a = 1;
print(a);
Int num(10);
cout << num;
cout << -num;
myvector vec(5);
#if 0
for (int i = 0; i < 5; i++)
{
cin >> vec[i];
}
for (int i = 0; i < 5; i++)
{
cout << vec[i];
}
#endif
Function p(print);
p();
//function<int(int,int)> pf(max);
//cout << pf(1, 2) << endl;
testAuto_ptr();
return 0;
}
本节课作业
1.自己整理笔记,带观点,和代码,代码用来验证观点
2.重载实战: 封装一个Array类,实现定长数组的操作
//以下测试代码要能够成功运行
Array array(4);
for(int i=0;i<array.size();i++)
{
cin>>array[i];
}
for(int i=0;i<array.size();i++)
{
cout<<array[i];
}
//实现数组的连接
Array one(3); //输入1 2 3
cin>>one;
Array two(4); //输入2 3 4
cin>>two;
Array sum=one+two;
cout<<sum<<endl; //打印1 2 3 2 3 4
Array num;
num=sum;
cout<<num<<endl;
C++类的组合
以另一个类的对象为数据成员
构造函数的写法,必须采用初始化参数列表的写法
#include <iostream> using namespace std; class Boy { public: Boy(string name, int age) :name(name), age(age) {} void print() { cout << name << "\t" << age << endl; } protected: void printData() { cout << name << "\t" << age << endl; } string name; int age; }; class MM { public: MM(string boyName, int boyAge, string mmName) :boy(boyName, boyAge) { this->mmName = mmName; } MM(string mmName) :mmName(mmName), boy("默认", 18) {} void print() { boy.print(); //boy.printData(); 不可访问,boy对于Boy是类外,不可直接访问保护属性 cout << mmName << endl; } protected: string mmName; Boy boy; }; int main() { MM mm("boy", 18, "mm"); mm.print(); return 0; }类的组合构造顺序问题: 构造顺序只和定义对象顺序的有关,和初始化参数列表无参
#include <iostream> using namespace std; class A { public: A(string str) :str(str) { cout << str; } string str; }; class B { public: B(string str) :str(str) { cout << str; } string str; }; class C { public: C(string str) :str(str) { cout << str; } string str; }; class D { public: D(string stra, string strb, string strc) :b(strb), c(strc), a(stra) { cout << "D" << endl; } A a; B b; C c; }; int main() { D d("A", "B", "C"); return 0; }类中类
依旧受权限限定
访问方式,需要类名限定
#include <iostream>
using namespace std;
struct Node
{
int data;
Node* next;
Node()
{
this->next = nullptr;
}
Node(int data)
{
this->next = nullptr;
this->data = data;
}
};
class List
{
public:
List()
{
headNode = new Node;
}
void push_front(int data)
{
Node* newNode = new Node(data);
newNode->next = headNode->next;
headNode->next = newNode;
}
protected:
Node* headNode;
public:
//迭代器-->类模仿指针行为
class iterator
{
public:
iterator(Node* pMove=nullptr) :pMove(pMove) {}
void operator=(Node* pMove)
{
this->pMove = pMove;
}
bool operator!=(Node* pMove)
{
return this->pMove != pMove;
}
iterator operator++()
{
pMove = pMove->next;
return iterator(pMove);
}
Node*& operator*()
{
return pMove;
}
protected:
Node* pMove;
};
Node* begin()
{
return headNode->next;
}
Node* end()
{
return nullptr;
}
};
//类中枚举类型
class A
{
public:
enum time {first,second};
protected:
enum date {mon,sur,tus};
//类中的枚举类型受权限限定
};
int main()
{
List list;
for (int i = 0; i < 3; i++)
{
list.push_front(i);
}
List::iterator iter;
for (iter = list.begin(); iter != list.end(); ++iter)
{
cout << (*iter)->data;
}
//cout << A::date::mon << endl; 不可访问
cout << A::time::first << endl;
return 0;
}
C++类中默认的函数
默认构造函数
默认拷贝构造函数
默认析构函数
默认赋值运算
#include <iostream>
using namespace std;
class A
{
public:
A() = default;
A(A& object) = default;
//void print() = default;
A& operator=(A& object) = default;
~A() = default;
};
int main()
{
A a;
A b = a;
A c;
c = a;
return 0;
}
C++ 封装Array
//以下测试代码要能够成功运行
Array array(4);
for(int i=0;i<array.size();i++)
{
cin>>array[i];
}
for(int i=0;i<array.size();i++)
{
cout<<array[i];
}
//实现数组的连接
Array one(3); //输入1 2 3
cin>>one;
Array two(4); //输入2 3 4
cin>>two;
Array sum=one+two;
cout<<sum<<endl; //打印1 2 3 2 3 4
Array num;
num=sum;
cout<<num<<endl;
答疑时间
运算符重载()
#include <iostream>
#include <algorithm>
class Function
{
public:
void operator()()
{
cout << "重载括号" << endl;
}
bool operator()(int a, int b) const
{
return a > b;
}
protected:
};
class Compare
{
public:
bool operator()(int a, int b) const
{
return a > b;
}
};
int main()
{
Function object;
object.operator()();
//让对象模仿函数的行为-->仿函数
object(); //括号运算符的隐式调用
cout << object(1, 2) << endl;
object.operator()(1, 2);
int array[4] = { 1,2,3,4};
sort(array, array + 4, Compare());
for (int i = 0; i < 4; i++)
{
cout << array[i] << "\t";
}
cout << endl;
return 0;
}
类重载不太懂
#include <iostream>
using namespace std;
class MM
{
public:
MM(string name="", int score=0) :name(name), score(score) {}
friend MM operator+(MM& one, MM& two) // operator+(mm, girl)
{
return MM(one.name, one.score + two.score);
}
protected:
string name;
int score;
};
/*
类的成员函数重载: 重载函数参数个数=操作数-1
友元函数重载: 参数个数=操作
*/
int main()
{
MM mm("baby", 49);
MM girl("baby", 49);
MM lisa = mm + girl; //隐式调用
//显示调用-->基本的函数调用 函数名(函数参数)
MM anni = operator+(mm, girl);
return 0;
}
new也可以重载
建议不要重载,用原生态的即可。
C++继承和派生
继承方式与权限问题
继承的写法
//父类 基类
class parent
{
};
//子类 派生类
//公有继承
class son1:public parent
{
public:
protected:
};
//保护继承
class son2:protected parent
{
public:
protected:
};
//私有继承
class son3:private parent
{
public:
protected:
};
//继承和派生
//继承: 子类中没有产生新的属性或者行为
//派生: 派生类中有新的属性和行为产生
class 子类名:继承方式 父类名
{
};
//继承方式 就是权限限定词
继承实质与权限问题
继承的实质: 父类的数据和成员子类中有一份
权限问题: 继承方式只会增强父类属性在子类中的权限显示
| public | protected | private | |
|---|---|---|---|
| protected继承 | protected | protected | 不可直接访问 |
| public继承 | public | protected | 不可直接访问 |
| private继承 | private | private | 不可直接访问 |
#include <iostream>
using namespace std;
class parent
{
public:
void print()
{
cout << name << "\t" << money << endl;
}
string& getWide()
{
return wife;
}
protected:
string name;
int money;
private:
string wife;
};
//子类
class son :public parent
{
public:
void printSon()
{
print();
cout << name << "\t" << money << endl;
//cout << wife << endl; 父类中私有属性不能直接访问
cout << getWide() << endl; //间接通过父类的函数访问
}
protected:
};
class A
{
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B :public A
{
public:
//int a1;
protected:
//int a2;
private:
//int a3; 不能直接访问
};
class C :protected A
{
public:
protected:
//int a1; //public 显示protected
//int a2;
private:
//int a3; 不能直接访问
};
class D :private A
{
public:
void print()
{
cout << a1 << endl;
cout << a2 << endl;
//cout << a3 << endl; //父类的私有属性不能直接访问
}
protected:
private:
//int a1;
//int a2;
//int a3; //父类的私有属性不能直接访问
};
//私有继承会导致当前父类 无法在孙子类有任何作用
class F :public D
{
public:
};
int main()
{
son boy;
boy.printSon();
B b;
b.a1 = 123;
C c;
//c.a1 = 12;
return 0;
}
继承中的构造函数
父类的属性通过父类的构造函数初始化
子类中的构造函数,必须要调用父类构造函数,必须采用初始化参数列表的方式
单继承和多继承
单继承: 只有一个父类
多继承: 两个或者两个以上的父类
继承的属性,无论被继承多少次,所以类一般不会被继承很多层,会导致类的臃肿
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
Parent() { cout << "父类无参构造函数" << endl; }
Parent(string FName, string SName):FName(FName), SName(SName){}
protected:
string FName;
string SName;
};
//单继承
class Son :public Parent
{
public:
//这种写法,父类必须存在无参的构造函数,当然缺省也可以
Son() { cout << "子类无参构造函数" << endl; }
Son(string FName, string SName, string sonSName) :Parent(FName,SName)
{
//自己的属性用什么办法初始化都行
this->sonFName = FName;
this->sonSName = sonSName;
}
void print()
{
cout << "父:" << FName + SName << endl;
cout << "子:" << sonFName + sonSName << endl;
}
protected:
string sonFName;
string sonSName;
//string FName;
//string SName;
};
//多继承
//欧田
//阳子
//欧阳 田子
class MM
{
public:
//MM() = default;
MM(string mmFName, string mmSName)
{
this->mmFName = mmFName;
this->mmSName = mmSName;
}
protected:
string mmFName;
string mmSName;
};
class GG
{
public:
//GG() = default;
GG(string ggFName, string ggSName)
{
this->ggFName = ggFName;
this->ggSName = ggSName;
}
protected:
string ggFName;
string ggSName;
};
class Girl :public GG,public MM
{
public:
//子类想要这种构造函数,每个父类都要有一个无参的构造函数
//Girl() {}
Girl(string mmFName, string mmSName, string ggFName, string ggSName)
:MM(mmFName,mmSName),GG(ggFName,ggSName)
{
girlFName = ggFName + mmFName;
girlSName = ggSName + mmSName;
}
void print()
{
cout << "父:" << ggFName + ggSName << endl;
cout << "母:" << mmFName + mmSName << endl;
cout << "女:" << girlFName + girlSName << endl;
}
protected:
string girlFName;
string girlSName;
};
//继承的属性一致都存在
class A
{
public:
A(int a) :a(a) {}
int a;
};
class B:public A
{
public:
B(int a,int b) :A(a),b(b) {}
int b;
};
class C :public B
{
public:
C(int a, int b,int c) :B(a,b),c(c) {}
int c;
};
class D :public C
{
public:
D(int a, int b, int c,int d) :C(a,b,c), d(d) {}
int d;
};
int main()
{
Son son; //子类构造对象,优先调用父类构造函数
Son son1("白", "老鬼", "日天");
son1.print();
Girl girl("阳", "子", "欧", "田");
girl.print();
return 0;
}
继承中同名问题
数据成员同名
成员函数名同名
正常赋值调用
非正常赋值调用
#include <iostream>
using namespace std;
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
void print()
{
cout << "MM:";
cout << name << "\t" << age << endl;
}
protected:
string name;
int age;
};
class Girl :public MM
{
public:
Girl(string name, int age) :MM("父类", 28), name(name), age(age) {}
void print()
{
//不做特别处理,就近原则
cout << name<<"\t" << age << endl;
//类名限定
cout << MM::name << "\t" << MM::age << endl;
//不做特别处理,就近原则
MM::print();
}
protected:
string name;
int age;
};
//虚继承 --->菱形继承
class A
{
public:
A(int a) :a(a) {}
protected:
int a;
};
class B :virtual public A
{
public:
B(int a, int b) :A(a), b(b) {}
protected:
int b;
};
class C :virtual public A
{
public:
C(int a, int c) :A(a),c(c) {}
void printC()
{
cout << a << endl;
}
protected:
int c;
};
class D :public C, public B
{
public:
//菱形继承,必须调用祖父的构造函数
D() :B(1, 2), C(3, 4) ,A(999)
{
}
void print()
{
//只有一个a 只有一份
cout << a << endl;
cout << B::a << endl;
cout << C::a << endl;
printC();
}
protected:
};
int main()
{
//不做特别处理,就近原则
//正常对象调用
Girl girl("girl", 18);
girl.print();
MM mm("mm", 28);
mm.print();
//正常的指针调用
//就近原则
Girl* pG = new Girl("newGirl", 19);
pG->print();
pG->MM::print();
MM* pM = new MM("newMM", 29);
pM->print();
//非正常的指针
//1.允许子类对象初始化父类指针
MM* pMM = new Girl("newGirl", 49);
pMM->print(); //父类的
//在没有virtual情况下,看指针类型
//在有virtual情况,看赋值对象
//2.父类对象初始化子类指针,不安全
//Girl* pGirl = new MM("newMM", 29); 错误
//Girl* pGirl = (Girl*)pM;
//pGirl->print(); 引发
D d;
d.print();
0;
}
构造顺序问题
单继承中的构造顺序问题
先构造父类的在构造子类的,析构顺序相反
多继承中构造顺序问题
任何构造顺序问题都和初始化参数列表无关
构造顺序和继承顺序一致
多继承中构造顺序问题
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A"; }
~A() { cout << "A" ; }
protected:
};
class B :public A
{
public:
B() { cout << "B"; }
~B() { cout << "B" ; }
};
class C
{
public:
C() { cout << "C"; }
~C() { cout << "C"; }
};
class D
{
public:
D() { cout << "D"; }
~D() {cout << "D";}
};
//构造顺序和继承顺序一致
class F :public C, public A, public D
{
public:
//任何构造顺序问题,都和初始化参数列表无关
F() { cout << "F";}
~F() { cout << "F";}
};
//CADF FDAC
int main()
{
{
B b; //ABBA
}
cout << endl;
F f;
return 0;
}
本节课作业
1.自己整理笔记,带观点,和代码,代码用来验证观点
2.设计一个类父类 Shape类 设计多个子类:Rect类 Circle类 分别求出并打印相应形状的周长和面积
3.设计类老师类,设计一个学生类,多继承产生一个研究生类,打印相关研究生的信息
答疑时间
子类对象初始化父类指针还有点不清楚
没有什么太多说法,C++允许这样做,你只需要知道会调用谁的方法即可
虚继承
满足菱形关系,才会有虚继承,只适用于菱形继承
欧阳田子的MMGG类在子类中的初始化
正常情况 同名问题处理
类中就近原则
在没有virtual情况下 ,类外看类型,有virtual看赋值对象
C++虚函数和多态
虚函数和虚函数表
什么是虚函数? 用virtual 修饰的成员函数叫做虚函数
虚函数对于类的影响
增加一个指针的内存,32位4个字节 ,64位就是8个字节
虚函数表(了解一下): 就是一个指针存储所有虚函数的首地址
#include <iostream>
using namespace std;
class MM
{
public:
virtual void print1()
{
cout << "虚函数1"<< endl;
}
virtual void print2()
{
cout << "虚函数2" << endl;
}
virtual void print3();
protected:
};
void MM::print3()
{
cout << "虚函数3" << endl;
}
class A
{
int age;
};
void testVirtual()
{
//C语言不允许存在空的结构体
cout << sizeof(MM) << endl; //空的类或者而结构体 占用1字节
cout << sizeof(A) << endl;
MM mm;
//虚函数表
int** vptr = (int **)&mm;
typedef void(*PF)();
PF func = (PF)vptr[0][0];
func(); //调用第一个虚函数
func = (PF)vptr[0][1];
func(); //调用第二个虚函数
}
int main()
{
testVirtual();
return 0;
}
虚函数和多态
多态定义: 同一种行为(调用)导致的不同的结果
多态的必要性原则
必须父类存在虚函数
子类必须采用public继承
必须存在指针的引用(使用)
#include <iostream>
using namespace std;
class Man
{
public:
void WC1()
{
cout << "男人上厕所" << endl;
}
virtual void WC2() //父类必须要有virtual
{
cout << "龌龊男人上厕所" << endl;
}
protected:
};
class Woman:public Man
{
public:
void WC1()
{
cout << "女人上厕所" << endl;
}
void WC2()
{
cout << "女人上厕所" << endl;
}
protected:
};
void testVirtual()
{
//正常访问不存在多态
cout << "正常访问,就近原则" << endl;
Man man;
man.WC1();
man.WC2();
Woman woman;
woman.WC1();
woman.WC2();
cout << "指针访问,正常赋值" << endl;
Man* pm = new Man;
pm->WC1();
pm->WC2();
Woman* pw = new Woman;
pw->WC1();
pw->WC2();
cout << "指针非正常赋值:子类对象初始化父类指针" << endl;
Man* parent = new Woman;
//有virtual看对象类型,没有virutal看指针
parent->WC1(); //不是虚函数
parent->WC2(); //是虚函数
parent = new Man;
parent->WC2();
}
//统一接口功能呢
void printInfo(Man* parent)
{
parent->WC2();
}
class Shape
{
public:
virtual void Draw()
{
cout << "绘制过程" << endl;
}
protected:
};
class Rect :public Shape
{
public:
void Draw()
{
cout << "画矩形" << endl;
}
protected:
};
class Circle :public Shape
{
public:
void Draw()
{
cout << "画圆" << endl;
}
protected:
};
class Triangle :public Shape
{
public:
void Draw()
{
cout << "绘制三角形" << endl;
}
};
class Ellipse :public Shape
{
public:
void Draw()
{
cout << "绘制椭圆" << endl;
}
};
//降低因为变化而要修改代码
//采用增加代码方式满足新需求
//统一接口
class Tool
{
public:
void draw(Shape* parent)
{
parent->Draw();
}
};
int main()
{
testVirtual();
printInfo(new Woman);
Tool* pTool = new Tool;
pTool->draw(new Circle);
pTool->draw(new Rect);
pTool->draw(new Triangle);
pTool->draw(new Ellipse);
return 0;
}
纯虚函数和ADT
纯虚函数也是虚函数只是纯虚函数是没有函数体的
virutal void print()=0; //在类中函数 这样写法
抽象类: 具有至少一个纯虚函数的类,叫做抽象类
抽象类不能构建对象
抽象类可以构建对象指针
纯虚函数没有被重写,无论被继承多少次 都是纯虚函数,虚函数无论被继承多少次都是虚函数
#include <iostream>
using namespace std;
//抽象类
class Parent
{
public:
virtual void print()= 0; //纯虚函数
protected:
};
void testAbstract()
{
//Parent object; 不能构建对象
Parent* parent = nullptr;
}
//纯虚函数就是做ADT(abstract data type 抽象数据类型)过程
//stack 栈
class stack
{
public:
//父类中所有的操作描述好
virtual void push(int data) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual bool empty() const = 0;
virtual int size() const = 0;
};
//子类想要创建对象,必须重写父类的纯虚函数
//ADT: 具有强迫性,所有子类重写函数必须和父类的一模一样
class arrayStack :public stack
{
public:
void push(int data)
{
}
void pop()
{
}
int top() const
{
return 1;
}
bool empty() const
{
return true;
}
int size() const
{
return 1;
}
//可以增加别的函数
//可以增加别的成员
protected:
int* array;
};
struct Node
{
int data;
Node* next;
};
class listStack :public stack
{
public:
void push(int data)
{
}
void pop()
{
}
int top() const
{
return 1;
}
bool empty() const
{
return true;
}
int size() const
{
return 1;
}
protected:
Node* headNode;
};
void testStack(stack* pStack)
{
pStack->push(1);
while (!pStack->empty())
{
cout << pStack->top();
pStack->pop();
}
}
class A
{
public:
virtual void print() = 0;
protected:
};
class B :public A
{
public:
void print()
{
cout << "B" << endl;
}
};
class C :public B
{
public:
void print()
{
cout << "C" << endl;
}
};
void Abtract()
{
//B b;
C c; //一般抽象类只被继承一次就重写
B* pc = new C;
pc->print();
}
int main()
{
testStack(new arrayStack);
testStack(new listStack);
Abtract();
return 0;
}
虚析构函数
#include <iostream>
using namespace std;
class parent
{
public:
//虚析构函数,不存在虚构造函数
virtual ~parent()
{
cout << "父类析构" << endl;
}
void print() {}
protected:
};
class son :public parent
{
public:
void print()
{
}
~son()
{
cout << "子类析构" << endl;
}
};
int main()
{
//在用子类对象初始化父类指针,父类需要虚析构函数做内存释放
parent* p = new son;
p->print();
delete p;
return 0;
}
C++类型转换
C++类型转换,专人做专事,传闻中C++中的更为安全。
const_cast
static_cast
dynamic_cast
reinterpret_cast
本节课作业
1.自己整理笔记,带观点,和代码,代码用来验证观点
C++IO流
流: 若干字节数据从一端到另一端我们叫做流
流类体系
流对象
流运算符 >> <<
输出输出流
ostream类
cout
cerr
clog
cin
字符类的处理
正常的操作
调用成员函数的方式
格式控制字符
包含头文件 iomanip
常用的格式控制,一种是调用成员函数方式,一种流控制字符去做
设置有效位数: setprecision(n)
设置精度: fixed结合setprecision使用
istream 类 cin
#include <iostream>
#include <cstdio>
#include <iomanip>
using namespace std;
void testostream() //output
{
//freopen()
cout << "标准输出" << endl; //重定向
cerr << "标准错误输出" << endl; //不能重定向
clog << "标准错误输出" << endl; //重定向为文件
//字符类的处理
cout.put('a');
cout << 'a' << endl;
char c = 'C';
cout.put(c);
cout << c << endl;
cout.write("ILoveyou",4); //指定长度,超过长度不做输出
//输入
//c=cin.get();
cout.put(cin.get());
//字符串
while (cin.get() != '\n');
//while(getchar()!='\n');
char str[20]="";
cin.getline(str, 20);
cout.write(str,20);
}
void testiomanip()
{
//格式控制
//设置格式
double pi = 34.12343;
cout << "设置有效位数是:" << setprecision(4) << pi << endl;
cout << "有效小数位:" << fixed << setprecision(4) << pi << endl;
//cout.precision(4); 所有的流控制符都会对应一个成员函数的方式
//进制输出
cout << hex << 32 << endl; //十六进制
cout << oct << 15 << endl; //8进制输出
cout << setbase(10) << 15 << endl; //8-16
//默认右对齐
//cout.with(8);
//cout << resetiosflags << endl;
cout << setiosflags(ios::left); //ios::right
cout << setw(8) << "123" << setw(8) << "12344" << setw(8) << "3444" << endl;
cout << setw(8) << "123" << setw(8) << "12344" << setw(8) << "3444" << endl;
}
void freopeniostream()
{
freopen("1.txt", "r", stdin);
int a, b;
scanf("%d%d", &a, &b); //输入由文件完成
//1 2 回车
freopen("2.txt", "w", stdout);
printf("%d", a + b);
}
int main()
{
//testostream();
testiomanip();
return 0;
}
字符流
用的头文件是:sstream类
istringstream类
ostringstream类
一般用时stringstream类对象即可
获取字符流对象中的数据
string str() //获取string
void str(const string& str); 重新设置字符流对象的数据
一般字符流对象做字符串处理
字符串分割
字符串转换问题
#include <iostream>
#include <sstream>
#include <cstdio>
using namespace std;
//23,132,3443,54,54,65
void teststringstream()
{
//构建字符流对象 ,以及获取字符流对象中数据
stringstream sso(string("ILoveyou"));
cout << sso.str() << endl;
stringstream ssnull;
ssnull << "我爱你!";
cout << ssnull.str() << endl;
//错误
//stringstream ss = string("IMissyou");
string data;
ssnull >> data;
cout << data << endl;
ssnull.str("ILoveyou");
//ssnull.clear();
cout << ssnull.str() << endl;
//字符串与数字之间转换
int num = 1234;
char input[20] = "";
stringstream transs(input);
transs << num;
transs >> input;
cout << input << endl;
stringstream snum("12345");
int temp = 0;
snum >> temp;
cout << temp << endl;
//分割
stringstream sData("23,132,3443,54,54,65");
int numData[6];
char cData[5];
for (int i = 0; i < 6; i++)
{
if (i == 5)
sData >> numData[i];
else
sData >> numData[i] >> cData[i];
}
for (int i = 0; i < 6; i++)
{
cout << numData[i] << " ";
}
cout << endl;
//多次同一个流做数据转换操作,一定做clear操作
transs.clear();
transs << num;
transs >> input;
cout << input << endl;
}
int main()
{
teststringstream();
//char str[20] = "";
//int num;
//scanf_s("%d%s", &num, str,20);
//cout << num << " " << str << endl;
return 0;
}
文件流
流类体系
ofstream 类 写操作 output输出到文件
ifstream 类 读操作
fstream类 可读可写 用的时候包含头文件 #include <fstream>
打开关闭文件
打开文件: void open(const char* URL,ios::openmode mode);
读写方法是
ios::in 读的方式打开文件
ios::out 写方式打开文件 具有创建功能
ios::app 追加模式 具有创建功能呢
ios::ate 追加模式,文件指针指向末尾
ios::trunc 具备创建功能
ios::nocreate 不具备创建
ios::noreplace 不替换 (想想C语言 w方式)
ios::binary 二进制的形式
读写的组合方式用的是 |
可读可写可创建: ios::in|ios::out|ios::trunc
二进制的可读可写课创建: ios::in|ios::out|ios::trunc|ios::binary
判断打开文件是否成功
用文件流对象重载的运算!
is_open成员函数判断
返回true 打开成功
返回false打开失败
关闭文件: void close()
读写文件
流的方式读写
二进制的方式读写
把string写到文件
需要先转换为char* 再写进去
文件指针定位
ifstream类的对象
istream& seekg(long int pos);
istream& seekg(long int pos,ios_base::seekdir position);
ofstream类的对象
ostream& seekp(long int pos);
ostream& seekp(long int pos,ios_base::seekdir position);
ios_base::seekdir
ios::beg 文件开始位置 //SEEK_SET
ios::end 文件结束位置
ios::cur 文件当前位置
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
void testOpenFile()
{
fstream file;
file.open("2.txt", ios::out);
if (!file.is_open())
{
cerr << "文件打开失败" << endl;
return;
}
if (!file)
{
cout << "文件打开失败" << endl;
return;
}
file.close();
}
void asciiRWFile(const char* readFileName, const char* writeFileName)
{
fstream read(readFileName, ios::in);
fstream write(writeFileName, ios::out);
//成员函数 eof() 再文件末尾
//1.流的方式读写
//1.1 流运算符读写 空格和换行会被忽略
//while (1)
//{
// char key;
// read >> key;
// if (read.eof())
// {
// break;
// }
// write << key;
//}
//1.2 成员函数
//while (1)
//{
// char key;
// read.get(key);
// if (read.eof())
// break;
// write.put(key);
//}
while (!read.eof())
{
char str[1024] = "";
read.getline(str, 1024);
write.fstream::write(str, strlen(str));
write.put('\n');
}
read.close();
write.close();
}
void binaryRWFile(const char* readFileName, const char* writeFileName)
{
fstream readFile(readFileName, ios::in | ios::binary);
fstream writeFile(writeFileName, ios::out | ios::binary);
while (!readFile.eof())
{
char str[1024] = "";
readFile.read(str, 1024);
writeFile.write(str, 1024);
}
readFile.close();
writeFile.close();
}
void testSeekRead(const char* fileName)
{
fstream fread(fileName, ios::in);
if (!fread)
{
cout << "打开文件失败!" << endl;
}
char key = fread.get();
cout << key;
fread.seekg(4,ios::beg);
key = fread.get();
cout << key;
fread.seekg(-4, ios::end);
key = fread.get();
cout << key << endl;
fread.close();
}
int main()
{
//testOpenFile();
//asciiRWFile("read.txt","write.txt");
//binaryRWFile("br.txt", "bw.txt");
testSeekRead("test.txt");
return 0;
}
本节课作业
1.自己整理笔记,带观点,和代码,代码用来验证观点 第一篇博客
2.C++类封装的形式写一个链式管理管理系统,带文件操作的,第二篇博客
C++异常处理
基本的异常处理
异常处理机制:暂缓问题处理,不在当前函数中处理,在他调用者中处理
什么是异常,任何东西都可以认为是异常,错误只是异常的一种
异常一旦被抛出,不做处理,如果引发异常,会调用默认abort终止程序
捕获和处理异常
throw 抛出异常,(可以理解为返回值,值是任何类型都可以,使我们处理异常一个参照)
try(检查,捕获)和catch(处理异常)
//try 与catch必须是一起出现,并且他们{}不能省略
try
{
//正常需要检查是否存在异常代码
}
catch(类型) //理解为switch中case语句
{
//处理是根据抛出数据类型决定如何处理
}
//一个try可以对应多个catch
try
{
//...
}
catch(int)
{
}
catch(double)
{
}
catch(string)
{
}
//catch和if else_if 执行机制是一样的,只能执行一个匹配项
//删减符 ... 任何类型异常都捕获
//catch(...)
不存在异常的描述
throw ()
noexcept
void print() throw()
{
cout << "当前函数不存在抛出异常操作" << endl;
}
void printData() noexcept
{
cout << "新的描述:不存在抛出异常" << endl;
//throw 0; 编译不过,一旦说明没有异常操作,就不能抛出
}
异常处理中的传参
catch(int a) //隐藏一个传参操作
想要处理抛出字符串的异常处理,注意一下string类型与const char* 类型的区别
也可以抛出自己类的对象
#include <iostream>
#include <string>
using namespace std;
class Error
{
public:
Error(const char* str = "未知错误") :str(str) {}
const char* what()const
{
return str.c_str();
}
protected:
string str;
};
int divisor(int a, int b)
{
if (b == 0)
throw "除数不能为0";
if(b==1)
throw "除数不能为1";
if(b==2)
throw string("除数不能为2");
return a / b;
}
void insertArray(int array[], int* curNum, int posData,int maxLength)
{
if (*curNum >= maxLength) //3>=3
{
throw Error("数组下标溢出!");
}
//0 1 2
array[*curNum] = posData; //array[3]=3
(*curNum)++;
}
int main()
{
try
{
divisor(1, 0);
}
catch (const char* str) //str= "除数不能为零";
{
cout << str << endl;
}
try
{
divisor(1, 2);
}
catch (string str)
{
cout << str << endl;
}
try
{
int array[3] = { 0,0,0 };
int curNum = 0;
for (int i = 0; i < 4; i++)
{
insertArray(array, &curNum, i, 3);
}
}
catch (Error str)
{
cout << str.what() << endl;
}
return 0;
}
自定义异常类
写一个类描述异常
继承标准库中的类描述异常
#include <iostream>
#include <string>
using namespace std;
//自己写的异常描述类
class Error
{
public:
Error(const char* str = "未知错误") :str(str) {}
const char* what()const
{
return str.c_str();
}
protected:
string str;
};
void insertArray(int array[], int* curNum, int posData, int maxLength)
{
if (*curNum >= maxLength) //3>=3
{
throw Error("数组下标溢出!");
}
//0 1 2
array[*curNum] = posData; //array[3]=3
(*curNum)++;
}
void testOne()
{
try
{
int array[3] = { 0,0,0 };
int curNum = 0;
for (int i = 0; i < 4; i++)
{
insertArray(array, &curNum, i, 3);
}
}
catch (Error str)
{
cout << str.what() << endl;
}
}
//继承标准库中的异常类
//通过重写what方法
class myException :public exception
{
public:
myException(string str) :exception(str.c_str()) {}
};
void insertArray(int a)
{
if (a >= 4)
throw myException("数组满了!");
cout << "插入成功" << endl;
}
void deleteArray(int a)
{
if (a <= 0)
throw myException("数组为空,无法删除");
cout << "删除成功" << endl;
}
void testTwo()
{
try
{
insertArray(1);
insertArray(4);
}
catch (myException& object)
{
cout << object.what() << endl;
}
try
{
deleteArray(1);
deleteArray(0);
}
catch (myException& object)
{
cout << object.what() << endl;
}
}
int main()
{
testOne();
testTwo();
return 0;
}
标注库中的异常
#include <exception>
#include <iostream>
using namespace std;
class Exception
{
public:
Exception(const char* ptr="UNKNOW") :ptr(const_cast<char*>(ptr)){}
virtual const char* what() const
{
return ptr;
}
protected:
char* ptr;
};
class Bad_alloc :public Exception
{
public:
Bad_alloc(const char* _Message = "bad exception") :Exception(_Message) {}
protected:
};
class Run_time :public Exception
{
public:
Run_time(const char* _Message = "run_time error") :Exception(_Message) {}
protected:
};
int main()
{
try
{
while (1)
{
int* p = new int[1024*1024*10];
}
}
catch (bad_alloc& object)
{
cout << object.what() << endl;
}
return 0;
}
上节课管理系统的作业
详细见代码
本节课作业
自己整理笔记,带观点,和代码,代码用来验证观点 第一篇博客
C++模板
函数模板
什么是模板 : 把类型当做未知量,可以忽略类型影响
声明模板的语法
//单个未知类型
template <typename _Ty> //_Ty 随便改 ,就是类型代号
_Ty Max(_Ty a, _Ty b)
{
return a > b ? a : b;
}
//可以多个未知类型
template <typename _Ty1,typename _Ty2 >
void print(_Ty1 one,_Ty2 two)
{
cout<<one<<endl;
cout<<two<<endl;
}
//typename 可以换成class
template <class T>
void print(T a)
{
cout<<a<<endl;
}
调用函数模板
隐式调用 : 正常的函数传参即可调用
显示调用: 函数名<类型名>(参数)
函数模板的两种形态
普通函数当做函数模板
类的成员函数是函数模板
函数模板特殊的写法
缺省写法
存在常量类型
#include <iostream>
using namespace std;
//int Max(int a, int b)
//{
// return a > b ? a : b;
//}
//string Max(string a, string b)
//{
// return a > b ? a : b;
//}
//引入模板这个东西
template <typename _Ty> //告诉编译器 下面代码用到一个位置类型叫做_Ty
_Ty Max(_Ty a, _Ty b)
{
return a > b ? a : b;
}
//_Ty= string a="abc" b="abc"
//普通函数
template <class _Ty1, class _Ty2>
void print(_Ty1 one,_Ty2 two)
{
cout << one << endl;
cout << two << endl;
}
class MM
{
public:
template <class _Ty>
void print(_Ty data)
{
cout << data << endl;
}
template <class _Ty>
void printData(_Ty data);
protected:
string name;
int age;
};
//在类外实现不能省略template这一块
template <class _Ty>
void MM::printData(_Ty data)
{
cout << data << endl;
}
//缺省写法
template <class _Ty1, class _Ty2 = int>
void printData(_Ty1 one, _Ty2 two)
{
cout << one<<"\t" << two << endl;
}
//存在传常量写法
//size_t: unsigned int 的别名
template <class _Ty1 ,size_t size=3>
void printArray(_Ty1 array) //_Ty1=int * ,size=3
{
for (int i = 0; i < size; i++)
{
cout << array[i];
}
cout << endl;
}
void testFunc()
{
printData("string", 1234);
//函数模板的缺省,显示调用,可以不用传类型,但是参数不能少
printData<string>("ILoveyou", 12344);
int array[3] = { 1,2,3 };
//没有做缺省必须显示调用
printArray<int*,3>(array);
//做了缺省可以隐式调用
printArray(array);
//不能传入变量,只能传入常量,函数模板如果存在变量的情况下
//int size = 3;
//printArray<int*, size>(array);
}
int main()
{
//隐式调用
cout << Max(1, 2) << endl;
cout << Max("string", "string1") << endl;
cout << Max(1.1, 2.3) << endl;
//显示调用
cout << Max<string>("abc", "abd") << endl;
print<string, string>("string1", "string2");
print<string, int>("string1", 1234);
//类中的成员函数是函数模板
MM mm;
mm.print(123);
mm.print<string>("ILoveyou");
mm.printData(12344);
//模板函数可不可以缺省
return 0;
}
类模板
生成一个类模板
template<class _Ty>
class MM
{
public:
protected:
}
//只要被template修饰就是一个模板类,用没用未知类型没关系
类模板调用
必须采用显式调用
类模板不是一个实际类型,所以所有用到类名的地方都需要使用: 类名<未知类型> 方式使用
多文件中,类模板 中的声明和实现一定在一起的,不能分开写。
#include <iostream>
using namespace std;
template <class _Ty>
class MM
{
public:
MM() {}
MM(string name):name(name) {}
void print();
protected:
string name;
};
//在类实现
template <class _Ty>
void MM<_Ty>::print()
{
cout << "类模板" << endl;
}
template<class _Ty>
class Girl :public MM<_Ty>
{
public:
Girl(string name) :MM<_Ty>(name)
{
}
protected:
};
class Boy
{
public:
};
template <class _Ty1, class _Ty2>
class Data
{
public:
Data(_Ty1 one, _Ty2 two) :one(one), two(two) {}
void print();
protected:
_Ty1 one;
_Ty2 two;
};
template <class _Ty1, class _Ty2>
void Data<_Ty1, _Ty2>::print()
{
cout << one << endl;
cout << two << endl;
}
int main()
{
//必须采用显式调用
MM<int> mm1;
MM<string> mm2;
MM<double> mm3;
//MM mm; 错误的
Girl<int> girl("Loveyuou");
girl.print();
Boy boy;
Data<string, int> mmInfo("小芳", 19);
mmInfo.print();
Data<int, int> data(12, 11);
data.print();
return 0;
}
自定义类型当做模板参数
基本自定义类型
自定义类型也是一个模板
模板传入自定义类型,关键点就在于重载运算符
#include <iostream>
using namespace std;
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
friend ostream& operator<<(ostream& out, const MM& mm)
{
out << mm.name << " " << mm.age;
return out;
}
bool operator>(MM& mm) const
{
return this->age > mm.age;
}
protected:
string name;
int age;
};
template <class _Ty>
void print(_Ty one)
{
//error C2679: 二进制“<<”: 没有找到接受“_Ty”类型的右操作数的运算符(或没有可接受的转换)
cout << one << endl;
}
template <class _Ty>
_Ty Max(_Ty a, _Ty b)
{
//rror C2676: 二进制“>”:“_Ty”不定义该运算符或到预定义运算符可接收的类型的转换
return a > b ? a : b;
}
template <class _Ty>
class Node
{
public:
Node(_Ty data, Node<_Ty>* next) :data(data), next(next) {}
_Ty getData()
{
return data;
}
Node<_Ty>* getNext()
{
return next;
}
protected:
_Ty data;
Node<_Ty>* next;
//正常写法:Node* next;
};
template <class _Ty>
class List
{
public:
List()
{
headNode = nullptr;
}
void insertList(_Ty data)
{
headNode = new Node<_Ty>(data, headNode);
}
void printList()
{
Node<_Ty>* pmove = headNode;
while (pmove != nullptr)
{
//error C2679: 二进制“<<”: 没有找到接受“_Ty”类型的右操作数的运算符(或没有可接受的转换)
cout << pmove->getData() << endl;
pmove = pmove->getNext();
}
cout << endl;
}
protected:
Node<_Ty>* headNode;
};
void testList()
{
List<int> list;
list.insertList(1);
list.insertList(2);
list.insertList(3);
list.printList();
List<MM> mmList;
mmList.insertList(MM("小芳", 18));
mmList.insertList(MM("小丽", 28));
mmList.insertList(MM("小美", 38));
mmList.printList();
}
int main()
{
//函数模板传入自定义类型
print(12);
print<string>("string");
print(MM("mm", 19));
MM xiaoF("小芳", 18);
MM xiaoL("小丽", 28);
MM result = Max<MM>(xiaoF, xiaoL);
//MM result = Max(xiaoF, xiaoL); 函数模板可以隐式调用
cout << result << endl;
testList();
return 0;
}
模板嵌套
明白类型是什么即可,适当可以借用using语法起别名 简化代码
#include <iostream>
using namespace std;
template <class _Ty1,class _Ty2>
class MM
{
public:
MM(_Ty1 one, _Ty2 two) :one(one), two(two) {}
friend ostream& operator<<(ostream& out, const MM& mm)
{
out << mm.one << " " << mm.two;
return out;
}
protected:
_Ty1 one;
_Ty2 two;
};
template <class _Ty1,class _Ty2>
class Data
{
public:
Data(_Ty1 one, _Ty2 two) :one(one), two(two) {}
void print()
{
cout << one <<" "<< two << endl;
}
protected:
_Ty1 one;
_Ty2 two;
};
void testFunc()
{
//_Ty1类型是:MM<string,int>
//_Ty2类型是:MM<double,double>
Data<MM<string, int>, MM<double, double>>
data(MM<string, int>("小芳",18), MM<double, double>(89,56));
data.print();
//上面两行 等效下面四行代码
MM<string, int> mmData("小芳", 18);
MM<double, double> mmScore(89, 56);
Data<MM<string, int>, MM<double, double>> mData(mmData, mmScore);
mData.print();
}
template <class _Ty>
void print(_Ty data)
{
cout << data << endl;
}
int main()
{
//隐式调用
print(MM<string, int>("小芳", 32));
//显示调用
//类型:MM<string, int>
print<MM<string, int>>(MM<string, int>("小美", 238));
//起别名简化代码
using MMType = MM<string, int>;
print<MMType>(MMType("小美", 238));
testFunc();
return 0;
}
函数模板重载
模板和普通函数 ,调用函数函数类型一致情况 优先调用普通函数
两个模板同时成立,优先调用类型相似度搞的那个
#include <iostream>
using namespace std;
void print(int a, string b)
{
cout << "普通函数" << endl;
}
template <class _Ty1,class _Ty2>
void print(_Ty1 a, _Ty2 b)
{
cout << "两个类型" << endl;
}
template <class _Ty>
void print(_Ty a, _Ty b)
{
cout << "一个类型" << endl;
}
int main()
{
print<int, string>(12, "显示调用百分调用模板");
print(12, string("优先调用适应的普通函数"));
//两个模板同时成立,优先调用类型相似度搞的那个
print(12, 12); //
return 0;
}
类模板特化
局部特化
完全特化
#include <iostream>
#include<string>
#include <tuple>
using namespace std;
//两未知类型
template <class _Ty1,class _Ty2>
class MM
{
public:
MM(_Ty1 one, _Ty2 two) :one(one), two(two) {}
void print()
{
cout << one << " " << two << endl;
}
protected:
_Ty1 one;
_Ty2 two;
};
class Data
{
public:
Data(int a, int b) :a(a), b(b) {}
void print()
{
cout << a << " " << b << endl;
}
protected:
int a;
int b;
};
//局部特化,特殊化
template <class _Ty>
class MM<_Ty,_Ty> //特化产生类,类名要用: 类名<类型> 方式使用
{
public:
MM(_Ty one, _Ty two) :one(one), two(two) {}
void print()
{
//cout << one << " " << two << endl;
one.print();
two.print();
cout << "特殊化" << endl;
}
protected:
_Ty one;
_Ty two;
};
//完全特化
template <>
class MM<string, string>
{
public:
MM(string one, string two) :one(one), two(two) {}
void print()
{
cout << "完全特化" << endl;
cout << one << " " << two << endl;
}
protected:
string one;
string two;
};
int main()
{
//原生模板
MM<string, int> mm1("小芳", 18);
mm1.print();
//局部特化模板
MM<Data, Data> dMM(Data(1,2),Data(3,4));
dMM.print();
//完全特化的模板
//折叠参数 后续讲元组容器的时候讲
MM<string, string> mm2("小丽", "小美");
mm2.print();
tuple<string, string> tp("张三", "李四");
return 0;
}
本节课作业
自己整理笔记,带观点,和代码,代码用来验证观点 第一篇博客
C++STL容器篇(一)
定长数组
array
#include <array>
#include <iostream>
#include <string>
using namespace std;
//size_t unsigned int
template <class _Ty,size_t size>
class MyArray
{
public:
MyArray()
{
memroy = new _Ty[size];
//new MM[size]
}
_Ty& operator[](int index)
{
return memroy[index];
}
~MyArray()
{
delete[] memroy;
}
public:
_Ty* begin()
{
return memroy + 0;
}
_Ty* end()
{
return memroy + size;
}
//类的对象模仿指针的行为
class iterator
{
public:
iterator(_Ty* pmove = nullptr) :pmove(pmove) {}
void operator=(_Ty* pmove)
{
this->pmove = pmove;
}
bool operator!=(_Ty* pmove)
{
return this->pmove != pmove;
}
iterator operator++(int)
{
this->pmove++;
return *this;
}
_Ty operator*()
{
return pmove[0];
}
protected:
_Ty* pmove;
};
protected:
_Ty* memroy; //MM
};
void testMyArray()
{
MyArray<int, 3> array1D;
for (int i = 0; i < 3; i++)
{
array1D[i] = i;
}
MyArray<int, 3>::iterator iter;
for (iter = array1D.begin(); iter != array1D.end(); iter++)
{
cout << *iter << "\t";
}
cout << endl;
}
void testArray()
{
//存储数据的类型是:int
//数组长度:3
//用模板的时候用的都是对象,而不是new一个对象
array<int, 3> array1D;
array<string, 3>* p = new array<string, 3>;
delete p;
#define MAX 5
array<double, 5> dAarray1D;
//创建并初始化
array<int, 3> num = { 1,2,3 };
for (int i = 0; i < array1D.size(); i++)
{
array1D[i] = i;
}
//迭代器
}
void testExOperator()
{
//使用: 和数组一样的用法
//一些函数
array<int, 3> test = { 1,2,3 };
cout << test.empty() << endl;
cout << test.size() << endl;
test.fill(5); //填充所有的元素 ,填充为5
for (int v : test)
{
cout << v << "\t";
}
cout << endl;
//交换 长度一定是要一样常
array<int, 3> test1 = { 0,0,0};
test.swap(test1);
int cData[3] = { 1,2,3 }; //映射:一种对应关系,数组下标对应元素
for (auto v : cData)
{
cout << v << "\t";
}
cout << endl;
}
//定长数组处理自定义类型的数据
class MM
{
public:
MM() {}
MM(string name, int age) :name(name), age(age) {}
void print()
{
cout << name << "\t" << age << endl;
}
protected:
string name;
int age;
};
void testUserData()
{
array<MM, 3> mmData; // MM array[3];
for (int i = 0; i < mmData.size(); i++)
{
string name = "name";
mmData[i] = MM(name + to_string(i), 16 + i);
}
for (auto v : mmData)
{
//v:就是MM的对象
v.print();
//cout<<v; //想要这样输出,重载<<运算符
}
//迭代器访问
//对象模仿指针,*迭代器 就是取值运算
array<MM, 3>::iterator iter;
//begin()
//end(): 最后一个位置,不是最后元素的位置
(*mmData.begin()).print();
//(*mmData.end()).print(); 越界访问
(*(mmData.end() - 1)).print();
for (iter = mmData.begin(); iter != mmData.end(); iter++)
{
//(*iter).print();
iter->print();
//cout<<*iter; //重载<<
}
}
//array当做函数参数,返回值都可以
array<int, 3>& returnArray(array<int, 3>& temp)
{
for (int i = 0; i < temp.size(); i++)
{
temp[i] = i;
}
return temp;
}
int main()
{
testArray();
testExOperator();
testUserData();
cout << "自己写的模板" << endl;
testMyArray();
return 0;
}
动态数组
vector
#include <vector>
#include <iostream>
#include <string>
using namespace std;
//辅助函数:遍历容器
template <class _Ty>
void printVector(vector<_Ty>& temp)
{
for (auto v : temp)
{
cout << v << "\t ";
}
cout << endl;
}
//基本用法
void testCreateVector()
{
//模板类型:存储数据类型
//1.不带长度的创建方式
vector<int> vecData;
//只能用成员函数做插入
for (int i = 0; i < 3; i++)
{
vecData.push_back(i); //尾插法 0 0 1 0 1 2
}
printVector(vecData);
//2.带长度
vector<string> strData(3); //当前动态数组的长度是3
//确定长度,可以直接使用数组法插入
for (int i = 0; i < 3; i++)
{
string name = "name";
//只有在确定长度范围以内的可以直接采用数组法插入
strData[i] = name + to_string(i);
}
printVector(strData);
//超过的必须用成员函数插入
//strData[3] = "name3"; vector subscript out of range 错误
strData.push_back("name3"); //在这个函数中做了自动扩增
printVector(strData);
//3.带初始化
vector<double> dData = { 1.1,1.23,1.44 }; //自动算出长度为3
printVector(dData);
//猜谜
vector<int> intData(3);
intData.push_back(1111); //在原来内存的后面扩增
printVector(intData); //0 0 0 1111
//迭代器遍历
vector<string>::iterator iter;
for (iter = strData.begin(); iter != strData.end(); iter++)
{
cout << *iter << "\t";
}
cout << endl;
}
//自定义类型数据
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
friend ostream& operator<<(ostream& out, const MM& temp)
{
out << temp.name << "\t" << temp.age;
return out;
}
protected:
string name;
int age;
};
void testUserData()
{
vector<MM> mmData;
for (int i = 0; i < 3; i++)
{
string name = "name";
mmData.push_back(MM(name + to_string(i), 18 + i));
}
//二进制“<<”: 没有找到接受“MM”类型的右操作数的运算符(或没有可接受的转换)
printVector(mmData);
}
void testExOperator()
{
vector<int> iData = { 1,2,3,4 };
cout << iData.size() << endl; //当前容器中的元素个数
cout << iData.empty() << endl; //判断是否为空 return size==0; 有元素返回false
cout << iData.front() << endl; //访问第一个元素
cout << iData.back() << endl; //访问最后一个元素
cout << iData.at(2) << endl; //下标访问
cout << iData[2] << endl; //和at(2)一样的效果 表示
//修改
iData.emplace(iData.begin() + 2, 100); //修改下标是2位置的元素为100
printVector(iData);
iData.emplace_back(999); //和push_back一样的功能
iData.emplace(iData.begin(), 1111); //修改第一个元素
//删除函数
//iData.erase(iData.begin() + 2); //数组只有伪删除,没有删除操作
printVector(iData);
//批量复制
int array[] = { 1,2,3 };
vector<int> vecData;
vecData.assign(array, array + 3); //不需要起始长度
printVector(vecData);
}
int main()
{
testCreateVector();
testUserData();
testExOperator();
return 0;
}
array与vector嵌套
#include <iostream>
#include <array>
#include <vector>
#include <string>
#include <ctime>
#include <cstdlib>
using namespace std;
void testArrayVsArray()
{
array<array<int, 3>, 4> arrData; //int arrData[4][3]
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
arrData[i][j] = i * j;
cout << arrData[i][j] << "\t";
}
cout << endl;
}
}
void testVectorVsVector()
{
srand((unsigned int)time(nullptr));
vector<vector<int>> vecData;
//一般vecotor 采用的是push_back插入
for (int i = 0; i < 4; i++)
{
vector<int> temp;
//rand()%3 [0,2] [2,4]
for (int j = 0; j < rand()%3+2; j++)
{
temp.push_back(i * j);
}
vecData.push_back(temp);
}
//不等列数的二位数组
for (int i = 0; i < vecData.size(); i++)
{
for (int j = 0; j < vecData[i].size(); j++)
{
cout << vecData[i][j] << "\t";
}
cout << endl;
}
}
void testArrayVsVector()
{
array<vector<int>, 3> vecArr;
vector<int> vec1[3] = { { 1,2,3 } , {1,2,3,4}, {1,2}};
for (int i = 0; i < vecArr.size(); i++)
{
vecArr[i] = vec1[i];
}
//不等列数的二位数组
for (int i = 0; i < vecArr.size(); i++)
{
for (int j = 0; j < vecArr[i].size(); j++)
{
cout << vecArr[i][j] << "\t";
}
cout << endl;
}
vector<array<array<vector<int>, 3>, 3>> vec;
//慢慢剥洋葱即可
array<array<vector<int>, 3>, 3> test;
for (int i = 0; i < 3; i++)
{
test[i] = vecArr; //vecArr: array<vector<int>, 3>
}
vec.push_back(test);
vector<array<array<vector<int>, 3>, 3>> test;
//上面一行 等效下面两行
using Data = array<array<vector<int>, 3>, 3>;
vector<Data> test2;
array<array<vector<int>, 3>, 3> test3;
//上面一行 等效下面两行
using Data2 = array<vector<int>, 3>;
array<Data2, 3> test3;
}
void testVectorVsArray()
{
vector<array<int, 3>> arrVec;
array<int, 3> arr[3] = { { 1,2,3 } , {1,2,3}, {1,2,3}};
for (int i = 0; i < 3; i++)
{
arrVec.push_back(arr[i]);
}
for (int i = 0; i < arrVec.size(); i++)
{
for (int j = 0; j < arrVec[i].size(); j++)
{
cout << arrVec[i][j] << "\t";
}
cout << endl;
}
}
int main()
{
testArrayVsArray();
testVectorVsVector();
testArrayVsVector();
testVectorVsArray();
return 0;
}
本节课作业
1.整理笔记,写好博客
C++STL容器篇(二)
双向链表
list
#include <list>
#include <iostream>
#include <string>
#include <functional> //less和greator头文件
using namespace std;
list<int>::iterator myFind(list<int>& iNum,int data)
{
for (list<int>::iterator iter = iNum.begin(); iter != iNum.end(); iter++)
{
if (*iter == data)
{
return iter;
}
}
return iNum.end();
}
//基本操作: 操作基本数据类型
void testList()
{
list<int> iNum;
list<string> strNum;
//插入
strNum.push_back("string1"); //尾插发
strNum.push_back("string2");
strNum.push_front("string3");
//string3 string1 string2
//遍历
//不删除方式遍历
list<string>::iterator iter;
for (iter = strNum.begin(); iter != strNum.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
cout << "是否为空:" <<boolalpha<< !strNum.empty() << endl;
cout << "元素个数:" << strNum.size() << endl;
//删除方式遍历
//string3 string1 string2
while (!strNum.empty())
{
cout << strNum.front() << " "; //back()
strNum.pop_front(); //头部删除 pop_front();
}
cout << endl;
cout << "元素个数:" << strNum.size() << endl;
//指定位置操作
//iterator find(iterator begin,iterator end,data);
for (int i = 0; i < 3; i++)
{
iNum.push_back(i);
}
auto result = find(iNum.begin(), iNum.end(), 2);
//没找到返回是end结束的位置
if (result == iNum.end())
{
cout << "未找到指定位置" << endl;
} //insert
iNum.insert(result, 100);
for (auto v : iNum)
{
cout << v << "\t";
}
cout << endl;
//删除函数
iNum.erase(result);
for (auto v : iNum)
{
cout << v << "\t";
}
cout << endl;
//其他操作
iNum.reverse(); //反转链表
for (auto v : iNum)
{
cout << v << "\t";
}
cout << endl;
iNum.sort(less<int>()); //排序
for (auto v : iNum)
{
cout << v << "\t";
}
cout << endl;
}
void testDelete()
{
int array[4] = { 1,2,2,3 };
list<int> data;
data.assign(array, array + 4);
//相同元素的删除
for (list<int>::iterator iter = data.begin(); iter != data.end(); )
{
if (*iter == 2)
{
iter = data.erase(iter);
}
else
{
iter++;
}
}
for (auto v : data)
{
cout << v << "\t";
}
cout << endl;
}
//操作自定义类型数据
class MM
{
public:
MM(string name, int age, int num) :name(name), age(age), num(num) {}
void print()
{
cout << name << "\t" << age << "\t" << num << endl;
}
bool operator==(const string& name) const
{
return this->name == name;
}
bool operator<(const MM& object) const
{
return this->name < object.name;
}
string getName() const
{
return name;
}
int getAge() const
{
return age;
}
protected:
string name;
int age;
int num;
};
bool compareByName(const MM& object1, const MM& object2)
{
return object1.getName() < object2.getName();
}
bool compareByAge(const MM& object1, const MM& object2)
{
return object1.getAge() < object2.getAge();
}
void testUserData()
{
list<MM> mmData;
string name;
int age;
int num;
while (1)
{
cout << "input MM:" << endl;
cin >> name >> age >> num;
mmData.push_back(MM(name, age, num));
cout << "是否继续输入?" << endl;
while (cin.get() != '\n');
if (cin.get() == 'n')
break;
}
cout << "姓名\t年龄\t编号" << endl;
for (MM v : mmData)
{
v.print();
}
//二进制“==”: 没有找到接受“MM”类型的左操作数的运算符(或没有可接受的转换)
auto result = find(mmData.begin(), mmData.end(), string("name1"));
//重载方式
mmData.sort(less<MM>()); //重载<
//mmData.sort(greater<MM>()); //重载>
//不采用重载方式,需要自己写比较准则
mmData.sort(compareByName);
mmData.sort(compareByAge);
}
int main()
{
//testList();
//testDelete();
testUserData();
return 0;
}
栈
stack
#include <stack>
#include <iostream>
#include <string>
using namespace std;
void testStack()
{
//穿脱原则
//FILO
//1 2 3
//3 2 1
//push(data)
//pop() 删除
//top() 获取栈顶元素
//size() empty();
stack<int> intStack;
for (int i = 0; i < 3; i++)
{
intStack.push(i);
}
while (!intStack.empty())
{
cout << intStack.top() << "\t";
intStack.pop();
}
cout << endl;
}
void NumTobinary(int data)
{
stack<int> bin;
while (data)
{
bin.push(data % 2);
data = data / 2;
}
if (bin.size() < 8)
{
for (int i = bin.size(); i < 8; i++)
{
bin.push(0);
}
}
while (!bin.empty())
{
cout << bin.top();
bin.pop();
}
cout << endl;
//中缀和后缀表达式资料看看
//a+b 中缀
//ab+
//a+b*c+d;
}
int main()
{
//testStack();
NumTobinary(512);
return 0;
}
队列
queue
#include <queue>
#include <iostream>
#include <string>
using namespace std;
void pop_queue(queue<int> qData)
{
while (!qData.empty())
{
cout << qData.front() << " "; //获取队头元素
qData.pop(); //出队
}
cout << endl;
}
void testQueue()
{
queue<int> qData;
for (int i = 0; i < 3; i++)
{
qData.push(i); //入队
}
while (!qData.empty())
{
cout << qData.front() << " "; //获取队头元素
qData.pop(); //出队
}
cout << endl;
cout << qData.size() << endl;
}
int main()
{
testQueue();
return 0;
}
deque
#include <deque>
#include <iostream>
#include <string>
using namespace std;
//从头出来
void pop_front_dequeue(deque<int> dData)
{
while (!dData.empty())
{
cout << dData.front()<<" ";
dData.pop_front();
}
cout << endl;
}
//从尾出来
void pop_back_dequeue(deque<int> dData)
{
while (!dData.empty())
{
cout << dData.back() << " ";
dData.pop_back();
}
cout << endl;
}
void testDeque()
{
deque<int> deData;
for (int i = 0; i < 3; i++)
{
deData.push_back(i); //尾插法入队
deData.push_front(i); //头插法入队
}
deData.push_back(999);
//0 0
//1 0 0 1
//2 1 0 0 1 2
pop_front_dequeue(deData);
pop_back_dequeue(deData);
}
int main()
{
testDeque();
return 0;
}
priority_queue
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
template <class _Ty,class _Container = vector<_Ty>,class _Pr = less<_Ty>>
class my_priority_queue
{
public:
protected:
};
void testCreatePriorityQueue()
{
//默认的方式,一级下面三种 都一样,大的先出队
priority_queue<int> pqData;
priority_queue<int,vector<int>> pqData2; //默认排序准则
priority_queue<int,vector<int>,less<int>> pqData3; //所有参数都完整
//优先队列,是按照数据的优先权出队,VIP服务
pqData.push(12);
pqData.push(0);
pqData.push(34);
while (!pqData.empty())
{
cout << pqData.top()<<" ";
pqData.pop(); //出队
}
cout << endl;
//贪心算法
priority_queue<int, vector<int>, greater<int>> pqDataG;
pqDataG.push(12);
pqDataG.push(0);
pqDataG.push(34);
while (!pqDataG.empty())
{
cout << pqDataG.top() << " ";
pqDataG.pop(); //出队
}
cout << endl;
}
int main()
{
testCreatePriorityQueue();
return 0;
}
本节课作业
1.整理笔记,写好第一篇博客
2.第二篇把管理系统改为list容器实现的,增加排序功能,按照不同的数据进行排序。
C++STL容器篇(三)
集合
set/multiset
#include <set>
#include <iostream>
#include <ctime>
using namespace std;
/*
set:集合
1.数据自带排序
2.数据唯一性
*/
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
bool operator<(const MM& object)const
{
return this->name < object.name;
}
void print()
{
cout << name << " " << age << endl;
}
protected:
string name;
int age;
};
void testSet()
{
srand((unsigned int)time(nullptr));
set<int> setData; //默认方式 从小到大
set<int, less<int>> setData2; //和默认方式一样
set<int, greater<int>> setData3; //从大到小
int array[10] = { 0 };
for (int i = 0; i < 10; i++)
{
int temp = rand() % 10;
array[i] = temp;
setData.insert(temp);
}
for (auto v : array)
{
cout << v << " ";
}
cout << endl;
for (set<int>::iterator iter = setData.begin(); iter != setData.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
void testUserData()
{
set<MM> mmData; //less<int> <
mmData.insert(MM("name3", 19));
mmData.insert(MM("name2", 28));
mmData.insert(MM("name3", 188));
for (auto v : mmData)
{
v.print();
}
set<char> cData;
set<string> strData;
}
//多重集合: 只具有排序功能,不具有去重功能
void testmultiset()
{
multiset<int> mulData;
for (int i = 0; i < 10; i++)
{
mulData.insert(rand() % 10);
//rand()%26+'A';
//rand()%26+'a';
}
for (auto v : mulData)
{
cout << v << " ";
}
cout << endl;
}
int main()
{
testSet();
testUserData();
testmultiset();
return 0;
}
bitset
#include <iostream>
#include <bitset>
using namespace std;
template <class _Ty,size_t size>
class MyArray
{
};
int main()
{
//多个二进制位
bitset<8> bData("11110000");
cout << bData << endl;
bData.flip();
cout << bData << endl;
cout << bData.all() << endl;
cout << bData.any() << endl;
cout << bData.size() << endl;
cout <<bData.none() << endl;
bitset<8> num(7);
cout << num << endl;
return 0;
}
映射
map/multiset
#include <map>
#include <iostream>
#include <graphics.h>
using namespace std;
template <class _Ty1, class _Ty2>
struct MyPair
{
_Ty1 first;
_Ty2 second;
MyPair(_Ty1 first, _Ty2 second) :first(first), second(second) {}
};
//map存储的数据是一个数对类型
void testPair()
{
pair<int, string> pData(1, "string");
MyPair<int, string> myPData(1, "string");
cout << pData.first << " " << pData.second << endl;
}
//map
//1.自带排序,默认是从小到大
//2.数据唯一性
void testMap()
{
map<int, string> mapData;
map<int, string, less<int>> mapData1; //和上面创建方式一样,从小到大
map<int, string, greater<int>> mapData2; //从大到小
//1.insert插入
mapData.insert(pair<int, string>(1, "string"));
//2.make_pair构建数对插入
mapData.insert(make_pair<int, string>(3, "string3"));
//3.单映射,可以直接采用数组下标方式进行插入
//数组在一定程序上来说或可以说数组
//map[first]=second;
//相比数组来说,这个下标是没有任何要求
mapData[-1] = string("string-1"); //等效插入一个数对类型
//上面代码等效:mapData.insert(pair<int,string>(-1,"string-1"))
mapData[1] = "string1"; //相同键 采用的是覆盖方式
//遍历:
for (auto iter = mapData.begin(); iter != mapData.end(); iter++)
{
//*iter指的是pair类型
cout << iter->first << " " << iter->second<<endl;
}
for (auto v : mapData)
{
cout << v.first << "\t" << v.second << endl;
}
//map<string, IMAGE*> img;
//img["墙"] = new IMAGE;
//img["路"] = new IMAGE;
//putimage(0, 0, img["墙"]);
//putimage(0, 0, img["路"]);
cout << mapData[1] << endl; //用的时候直接使用即可
mapData.erase(1);
for (auto v : mapData)
{
cout << v.first << "\t" << v.second << endl;
}
cout << endl;
}
class MM
{
public:
MM() = default;
MM(string name, int age) :name(name), age(age) {}
void print() const
{
cout << name << "\t" << age << endl;
}
bool operator<(const MM& object)const
{
return this->name < object.name;
}
protected:
string name;
int age;
};
class Boy
{
public:
Boy() = default;
Boy(string name, int age) :name(name), age(age) {}
void print() const
{
cout << name << "\t" << age << endl;
}
protected:
string name;
int age;
};
void testUserData()
{
map<MM, Boy> mbData;
mbData[MM("小美", 19)] = Boy("小张", 29);
mbData[MM("小美", 19)].print();
mbData[MM("小丽", 20)] = Boy("小明", 18);
cout << "配对信息:" << endl;
for (auto v : mbData)
{
//容器管理自定义类型数据
//v:pair<MM,Boy>
//v.frist:MM
//v.second:Boy
v.first.print();
v.second.print();
}
}
void testmultimap()
{
//多重映射,没有什么限制,什么样对应关系都可以插入到映射中
//因为存在相同的键,所以不能采用下标法
multimap<int, string> mulData;
mulData.insert(pair<int, string>(1, "string"));
mulData.insert(pair<int, string>(1, "string1"));
mulData.insert(pair<int, string>(2, "string"));
mulData.insert(pair<int, string>(3, "string"));
mulData.insert(make_pair<int, string>(3, "string"));
for (auto v : mulData)
{
cout << v.first << "\t" << v.second << endl;
}
}
int main()
{
testMap();
testUserData();
testmultimap();
return 0;
}
列表
initializer_list
#include <array>
#include <list>
#include <vector>
#include <iostream>
#include <initializer_list>
using namespace std;
class MM
{
public:
MM(string a, string b, string c) :a(a), b(b), c(c) {}
MM(const initializer_list<string>& list)
{
for (auto iter = list.begin(); iter != list.end(); iter++)
{
cout << *iter << endl;
}
}
protected:
string a;
string b;
string c;
};
void print(initializer_list<int> list)
{
for (auto iter = list.begin(); iter != list.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
int main()
{
array<int, 3> arrData = { 1,2,3 };
vector<int> vecData1 = { 1,2,3,4 };
vector<int> vecData2 = { 1,2,3,4,5 };
MM mm1 = { "string1","string2","string3" };
MM mm2 = { "string1" };
MM mm3 = { "string1","string2" };
initializer_list<int> listOne = { 1,2,3,4,5 };
initializer_list<int> listTwo = { 1,2,3};
print({ 1 });
print({ 1,2 });
print({ 1,2,3,4 });
print({ 1,2,3,4,5 });
return 0;
}
元组
tuple
#include <tuple>
#include <iostream>
using namespace std;
void testTuple()
{
//把任何类型的一系列数据当做一组处理
tuple<string, int, int, string> mmInfo = {"MM",18,1001,"123445"};
tuple<double, double, double> mmscore = make_tuple(98, 98, 88);
tuple<string, string> value = forward_as_tuple("小张", "小美");
tuple<string, int, int, string> array[3];
}
void visitedData()
{
tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
//get方法,不能用for循环
cout << get<0>(mmInfo) << "\t";
cout << get<1>(mmInfo) << "\t";
cout << get<2>(mmInfo) << "\t";
cout << get<3>(mmInfo) << endl;
//tie的方式访问数据
string name;
int age;
int num;
string tel;
tie(name, age, num, tel) = mmInfo;
cout << name << "\t" << age << "\t" << num << "\t" << tel << endl;
}
void Exoperator()
{
tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
tuple<double, double, double> mmscore = make_tuple(98, 98, 88);
auto result = tuple_cat(mmInfo, mmscore);
}
int main()
{
visitedData();
return 0;
}
折叠参数(拓展)
折叠参数: ...
可增长模板参数 模板函数
可增长模板参数 类模板
本节课作业
1.整理笔记,写好第一篇博客
C++STL迭代器
迭代器
迭代器: 就是一个类中类,通过运算符重载通过类中类的对象去遍历容器
迭代器的分类
正向迭代器: iterator
begin();
end();
反向迭代器: reverse_iterator
rbegin();
rend();
常正向迭代器: const_iterator
cbegin();
cend();
常反向迭代器:const_reverse_iterator
crbegin();
crend();
按功能分类
正向迭代
双向迭代
随机访问迭代器
容器中迭代器分类
容器名 迭代器类型 array 随机访问 vector 随机访问 deque 随机访问 stack/queue/priority_queue(特定顺序存取) 不支持 list 双向 set/multiset 双向 map/multimap 双向 迭代器辅助函数
移动:advance(iterator iter,n);
间距:distance(iterator begin,iterator end);
交换:iter_swap(iterato first,iterator end);
拓展内容:特殊迭代器 流型迭代器-->一般用在辅助打印
输出流型
ostream_iterator<_Ty> iter(ostream& out);
ostream_iterator<_Ty> iter(ostream& out,char* str);
输出流型迭代做赋值运算,意味着就是打印数据到屏幕上
输入流型
istream_iterator<_Ty> iter; //构造无参对象,是一个错误流 end_of_ostream
istream_iterator<_Ty> iter(istream& in);
*iter 等效cin>>操作
Lambda表达式
Lambda: 就是一个返回函数指针的表达式,它定义和返回值函数指针在一起的
Lambda表达式的组成部分
// [捕获方式](函数参数)mutable exception->函数返回值类型{函数体;}
int Max(int a,int b)
{
return a>b?a:b;
}
void print()
{
//Max的Lambda表达式写法
int(*pMax)(int,int)=[](int a,int b)mutable noexcept->int{ return a>b?a:b;};
//省略写法: 中间描述此都可以省略
auto ppMax=[](int a,int b){reutrn a>b?a:b;};
}
//捕获方式-->函数使用外部的变量的方式
/*
[=] //值的方式捕获
[&] //引用的方式捕获
[this]//this指针方式捕获
[] //不捕获任何变量
[=,&x];//x用引用方式捕获,其他变量用值的方式捕获
*/
#include <iostream>
using namespace std;
int Max(int a, int b)
{
return a > b ? a : b;
}
void print(int(*pMax)(int, int), int a, int b)
{
cout << pMax(a, b) << endl;
}
class MM
{
public:
void print()
{
[this] {cout << name << "\t" << age << endl; }(); //定义和调用一步到位
}
protected:
string name="默认";
int age=100;
};
int main()
{
int(*pMax)(int, int) = nullptr;
//完整版:Lambad 表达式
//final override
pMax = [](int a, int b)mutable noexcept->int {return a > b ? a : b; };
cout << pMax(1, 3) << endl;
//省略版本的,写代码越简单越好
auto pp = [](int a, int b) {return a > b ? a : b; };
cout << pp(1, 3) << endl;
//实际使用可以一步到位--->短暂性局部使用的的函数
cout << [](int a, int b)mutable noexcept->int {return a > b ? a : b; }(1, 3) << endl;
print([](int a, int b) {return a > b ? a : b; }, 1, 3);
print([](int a, int b) {return a > b ? b : a; }, 1, 3);
print([](int a, int b) {return a+b; }, 1, 3);
//捕获方式的区别
//用值的捕获: 在Lambad中不能把值当做左值使用,函数调用不会因为值的改变而改变
int data = 101010;
auto pFunc = [=] { cout << data << endl; }; //无参() 可以省略
auto pFunc2 = [&] { cout << data << endl; };
pFunc();
pFunc2();
data = 808080;
pFunc();
pFunc2();
MM mm;
mm.print();
//特殊的东西-->结合auto使用
auto pAuto = [](auto a, auto b) ->auto{return a > b ? a : b; };
cout << pAuto(1, 3) << endl; //[](auto a, auto b) ->auto{return a > b ? a : b; }(1,3)
cout << pAuto("stringa", "stringb") << endl;
//[] (auto a, auto b) ->auto{return a > b ? a : b; }("stringa", "stringb");
return 0;
}
仿函数
什么仿函数? 类模仿函数调用行为,实质是无名对象调用重载的()函数
所以仿函数的关键点在于重载()
一般情况仿函数是做排序准则,或者一些算法的计算准则
标准库中的仿函数
算术类
关系类
逻辑类
选择,证同,投射
#include <iostream>
#include <string>
#include <functional> //仿函数所在头文件
#include <map>
using namespace std;
class Sum
{
public:
int operator()(int a, int b) const
{
return a + b;
}
};
int main()
{
//重载的()的调用方式
Sum s;
cout << "显式调用:" << s.operator()(1, 3) << endl; //显式调用重载函数
cout << "隐式调用:" << s(1, 3) << endl;
//用{}和()帮助编译器去做解析
cout << "无名调用:" << Sum{}(1, 3) << endl; //类模仿函数调用行为--->仿函数
cout << "无名调用:" << Sum()(1, 3) << endl; //类模仿函数调用行为--->仿函数
//算术
cout << plus<int>{}(1, 3) << endl;
//关系
cout << equal_to<int>{}(1, 3) << endl;
map<int, less<int>> map1;
map<int, greater<int>> map3;
//逻辑类
cout << logical_and<int>{}(1,0) << endl;
//求大于3 小于10的数字
//没必要的做的事情
int a = 34;
if (logical_and<int>{}(a > 3, a < 10))
{
cout << "大于3 小于10的数字" << endl;
}
return 0;
}
函数适配器
什么是函数适配器: 用来绑定函数调用时候的参数,让函数适应其他调用的用法
#include <iostream>
#include <string>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
//老版本bind1st bind2nd
//新版本: bind函数
int Max(int a, int b)
{
return a > b ? a : b;
}
void print(int(*pMax)(int,int), int a,int b)
{
cout << pMax(a,b) << endl;
}
class Test
{
public:
void print(int a, int b, int c)
{
cout << a << " " << b << " " << c << endl;
}
protected:
};
void testClassFunc()
{
Test test;
auto testFunc = bind(&Test::print, &test, std::placeholders::_1, std::placeholders::_2, 99);
testFunc(1, 3); //调用,直接调用
}
void printData(int one, Test two, string str)
{
cout << "调用成功" << endl;
}
//可以通过占位符,所以调整参数位置,形成不同的调用形态
void testExUser()
{
//占位符代表原函数的参数 在调用形态 的第二个位置
auto testFunc = bind(printData,std::placeholders::_3, std::placeholders::_1, std::placeholders::_2);
printData(1, Test(), "ILoveyou");
testFunc(Test(), "ILoveyou", 1);
}
int main()
{
cout << Max(1, 3) << endl;
//基本用法
//std::placeholders::_1占位符
auto pMax = bind(Max, std::placeholders::_1, 100); //把第二个参数置为100
//只是增加调用行为,并没有真正改变了这个函数指针类型
cout << pMax(34) << endl;
cout << pMax(13, 44) << endl; //绑定后的函数指针,不再支持传参的,第二个参数无效
//语法上没问题,但是尽量别这样做
using namespace std::placeholders;
auto pMax2 = bind(Max, _1, 100); //把第二个参数置为100
cout << pMax2(34) << endl;
vector<int> vecData = { 19,43,89,89,34,54,67,54 };
cout << count_if(vecData.begin(), vecData.end(),bind(greater<int>(),std::placeholders::_1,60)) << endl;
cout << count_if(vecData.begin(), vecData.end(), [](int a) {return a > 60; }) << endl;
testClassFunc();
testExUser();
return 0;
}
函数包装器
函数包装器是什么?就是把函数指针包装成一个对象。通过这个对象调用函数
一旦函数指针被函数包装器包装了,那这个包装器对象可以直接替换函数指针的用法去调用函数
函数包装器类的实力例化传参: function<函数返回值类型(参数类型)>
#include <iostream>
#include <string>
#include <functional>
using namespace std;
int Max(int a, int b)
{
cout << "包装普通函数:" << endl;
return a > b ? a : b;
}
class MM
{
public:
void print(int a) //void print(int a, MM* mm);
{
cout << "包装成员函数" << endl;
}
static void printStatic()
{
cout << "包装静态的函数" << endl;
}
protected:
};
void testMMFunc()
{
MM mm;
function<void(int)> func(bind(&MM::print,&mm,std::placeholders::_1));
func(12);
}
class Test
{
public:
void operator()(string str)
{
cout << str << endl;
}
};
void printData(int a, MM mm, string str)
{
cout << "bind和function" << endl;
}
void TestFuncBind()
{
function<void(string, int, MM)> pf = bind(printData,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_1 );
pf("string", 1, MM());
//3 1 2
}
int main()
{
function<int(int, int)> funcMax(Max);
cout << funcMax(1, 3) << endl;
function<int(int, int)> funcMax2=Max;
function<void()> funcS(MM::printStatic);
funcS();
//仿函数包装
Test test;
function<void(string)> func = test;
func("包装仿函数");
TestFuncBind();
//包装成员函数
testMMFunc();
return 0;
}
1.整理笔记,写好第一篇博客
C++STL算法篇
STL查找算法
基本查找
find:区间查找
find_if:条件查找
find_firt_of: 查找区间第一次出现值
adjacent_find: 查找第一次重复的数
search:子序列查找
search_n: 子序列查找出现次数
统计查找
count: 区间统计
count_if: 条件统计个数
equal:比较
有序查找
binary_search:二分查找
upper_bound: 查找最后一个大于查找的值
lower_bound: 大于等于查找的值
equal_range:区间比较---有序序列
STL排序通用算法
merge: 归并排序,存于新容器
inplace_merge: 归并排序,覆盖原区间
sort: 排序,更改原容器顺序
stable_sort: 排序,保存原容器数据顺序
nth_element: 关键字排序
partition:范围排序
partial_sort:范围排序
partial_sort_copy:范围排序外加复制操作
stable_partition: 范围排序,保存原容器顺序
random_shuffle: 随机排序
reverse:逆序原容器
reverse_copy: 逆序容器保存到新容器
rotate:移动元素到容器末尾
rotate_copy:移动元素到新容器
STL删除替换算法
copy: 拷贝函数
copy_backward: 逆序拷贝
iter_swap: 交换
remove: 删除
remove_copy: 删除元素复制到新容器
remove_if:条件删除
remove_copy_if:条件删除拷贝到新容器
replace:替换
replace_copy: 替换,结果放到新容器
replace_if: 条件替换
replace_copy_if:条件替换,结果另存
swap: 交换
swap_range:区间交换
unique:去重
unique_copy:去重,结果另存
STL排列组合算法
next_permutation:下一个排序序列的组合
prev_permutation:上一个排序序列的组合
STL 算术算法
accumulate:区间求和
partial_sum:相邻元素的和
inner_product:序列内积运算
adjacent_difference:相邻元素的差
STL 生成异变算法
for_each:迭代访问
fill:填充方式初始容器
fill_n:指定长度填充容器
generate_n:填充前n个位置
transform:一元转换和二元转换
STL 关系算法
equal:两容器元素是否都相同
includes:是否是包含关系
lexicographical_compare:比较两个序列
max:求最大值
max_element:返回最大值的iterator
min:求最小值
min_element:求最小值的iterator
mismatch:找到第一个不同的位置
STL 集合算法
set_union:差集
set_intersection:并集
set_difference:保存第一个中有第二个没有的元素
set_symmetric_difference:对称差集
STL堆算法
make_heap:生成一个堆
pop_heap:出堆
push_heap:入堆
sort_heap:堆排序
C++智能指针
智能指针
智能指针其实本质是一个模板类,一般使用是用的这个类的对象,而不是指针
智能指针体现在内存释放问题。用智能指针管理new的对象, 将不在需要手动delete
shared_ptr
get() 函数: 返回数据的指针的引用
use_count(): 返回的是管理对象的智能指针对象数
swap():交换管理对象
reset():重置管理对象
weak_ptr
弱引用指针,不会累计计数
weak_ptr只能通过shared_ptr或者weak_ptr来构造
主要应用场景: 为了解决shared_ptr 循环引用内存导致无法释放问题
不可使用* 取值,能使用->取值
通过成员函数lock获取shared_ptr对象 然后再访问数据
unique_ptr
禁止拷贝和赋值,独占型
任何时候unqiue_ptr操作管理对象,永远都只有一个有效
可以通过move函数转交所有权
reset函数结合release函数移交所有权
C++正则表达式
正则是一种规则,它用来匹配(进而捕获、替换)字符串。这种规则需要“模式”、“字符串”这两样东西,“模式”根据正则规则,来处理“字符串”。这种规则被许多语言支持,C++11以后才支持正则。
具有特殊意义的元字符
\:\字符能够改变字符原本的含义
^:^字符指示字符串的头,且要求字符串以字符开头,不占位。\^表示一个真正的^符号。
$:$字符指示字符串的尾,且要求字符串以字符结尾,不占位。\$表示一个真正的$符号。
():分组,大正则中包含小正则。可以改变默认的优先级。在模式中可以使用\1来表示第一组已然捕获到的东西。
\b:指示字符串的边界(头/尾/空格左/空格右),字符\b要求边界的左边是字符,\b字符要求边界的右边是字符。
.:表示一个除了\n以外的任意一个字符。\.表示一个真正的.符号。
|:a|b a或b之一
[abc]:abc之中的任意一个
[^abc]: abc之外的
[a-z]: 任意小写字母
[^a-z]: 除了小写字母之外的
\w:任意一个字母数字下划线,等价于[(0-9)(a-z)(A-Z)(_)]
\W:字母数字下划线之外的,等价于[]
\d: 任意一个数子
\D: 除了数字之外的
\s: 空白符(空格、制表符、换页符)
量词元字符
*:字符*要求字符出现0到多次 {0,}
+:字符+要求字符出现1到多次 (\w) {1,}
?:字符?要求字符出现0次或1次 {0,1}
{n}:字符{n}要求字符出现n次
{n,}:字符{n,}要求字符出现n到多次 {0,}
{n,m}:字符{n,m}要求字符出现n到m次、
所以含有\的元字符,在C++定义时,都要写成\\
校验数字的表达式
数字:^ [0 - 9] * $ n位的数字:^ \d{ n }$ 至少n位的数字:^ \d{ n, }$ m - n位的数字: ^ \d{ m,n }$ 零和非零开头的数字: ^ (0 | [1 - 9][0 - 9] *)$ 非零开头的最多带两位小数的数字: ^ (\[1 - 9][0 - 9] *) + (.[0 - 9]{ 1,2 }) ? $ 带1 - 2位小数的正数或负数: ^ (\ - ) ? \d + (\.\d{ 1,2 }) ? $ 正数、负数、和小数: ^ (\ - | \ + ) ? \d + (\.\d + ) ? $ 有两位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 2 }) ? $ 有1~3位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 1,3 }) ? $ 非零的正整数: ^ [1 - 9]\d * $ 或 ^ ([1 - 9][0 - 9] *) { 1, 3 }$ 或^ \ + ? \[1 - 9][0 - 9] * $ 非零的负整数: ^ \ - [1 - 9][]0 - 9"$ 或 ^-[1-9]\d$ 非负整数: ^ \d + $ 或 ^ [1 - 9]\d * | 0$ 非正整数: ^ -[1 - 9]\d * | 0$ 或 ^ ((-\d + ) | (0 + ))$ 非负浮点数: ^ \d + (.\d + ) ? $ 或 ^ [1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * | 0 ? .0 + | 0$ 非正浮点数: ^ ((-\d + (.\d + ) ? ) | (0 + (.0 + ) ? ))$ 或 ^ (-([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d*)) | 0 ? \.0 + | 0$ 正浮点数: ^ [1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * $ 或 ^ (([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9] + ) | ([0 - 9] * [1 - 9][0 - 9] *))$ 负浮点数: ^ -([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d*)$ 或 ^ (-(([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9]) | ([0 - 9] * [1 - 9][0 - 9] *)))$ 浮点数: ^ (-? \d + )(.\d + ) ? $ 或 ^ -? ([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * | 0 ? .0 + | 0)$
校验字符的表达式
汉字: ^ [\u4e00 - \u9fa5]{ 0, }$ 英文和数字: ^ [A - Za - z0 - 9] + $ 或 ^ [A - Za - z0 - 9]{ 4,40 }$ 长度为3 - 20的所有字符: ^ .{3, 20}$ 由26个英文字母组成的字符串: ^ [A - Za - z] + $ 由26个大写英文字母组成的字符串: ^ [A - Z] + $ 由26个小写英文字母组成的字符串: ^ [a - z] + $ 由数字和26个英文字母组成的字符串: ^ [A - Za - z0 - 9] + $ 由数字、26个英文字母或者下划线组成的字符串: ^ \w + $ 或 ^ \w{ 3,20 }$ 中文、英文、数字包括下划线: ^ [\u4E00 - \u9FA5A - Za - z0 - 9_] + $ 中文、英文、数字但不包括下划线等符号: ^ [\u4E00 - \u9FA5A - Za - z0 - 9] + $ 或 ^ [\u4E00 - \u9FA5A - Za - z0 - 9]{ 2,20 }$ 可以输入含有 ^ %&',;=?$"等字符:[^%&', ; = ? $\x22] + 12 禁止输入含有~的字符:[^ ~\x22] +
特殊需求表达式
Email地址: ^ \w + ([-+.]\w + ) * @\w + ([-.]\w + ) * .\w + ([-.]\w + ) * $ 域名:[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }(/ .[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }) + / . ? InternetURL:[a - zA - z] + ://\s* 或 ^http://([\w-]+.)+[\w-]+(/[\w-./?%&=])?$ 手机号码: ^ (13[0 - 9] | 14[5 | 7] | 15[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9] | 18[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9])\d{ 8 }$ 电话号码(0511 - 4405222、021 - 87888822):\d{ 3 } - \d{ 8 } | \d{ 4 } - \d{ 7 } 身份证号(15位、18位数字): ^ \d{ 15 } | \d{ 18 }$ 短身份证号码(数字、字母x结尾): ^ ([0 - 9]) { 7, 18 }(x | X) ? $ 或 ^ \d{ 8,18 } | [0 - 9x]{ 8,18 } | [0 - 9X]{ 8,18 } ? $ 帐号:(字母开头,允许5 - 16字节,允许字母数字下划线): ^ [a - zA - Z][a - zA - Z0 - 9_]{ 4,15 }$ 密码:(以字母开头,长度在6~18之间,只能包含字母、数字和下划线): ^ [a - zA - Z]\w{ 5,17 }$ 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8 - 10之间): ^ (? = .\d)(? = .[a - z])(? = .*[A - Z]).{8, 10}$ 日期格式: ^ \d{ 4 } - \d{ 1,2 } - \d{ 1,2 } 一年的12个月(01~09和1~12): ^ (0 ? [1 - 9] | 1[0 - 2])$ 一个月的31天(01~09和1~31): ^ ((0 ? [1 - 9]) | ((1 | 2)[0 - 9]) | 30 | 31)$ xml文件: ^ ([a - zA - Z] + -? ) + [a - zA - Z0 - 9] + \\.\[x | X]\[m | M][l | L]$ 中文字符的正则表达式:[\u4e00 - \u9fa5] 双字节字符:\[^ \x00 - \xff](包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)) 空白行的正则表达式:\n\s * \r(可以用来删除空白行) HTML标记的正则表达式:<(\S* ? ) > >. ? < / \1> | <.* ? / > (复杂的嵌套标记依旧无能为力) 首尾空白字符的正则表达式: ^ \s * | \s * $或(^ \s*) | (\s * $) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等)) 腾讯QQ号:[1 - 9][0 - 9]{ 4, } (腾讯QQ号从10000开始) 中国邮政编码:[1 - 9]\d{ 5 }(? !\d) (中国邮政编码为6位数字) IP地址:\d + .\d + .\d + .\d + (提取IP地址时有用) IP地址:((? : (? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d)\.) { 3 }(? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d))
C++时间管理
时间段
表示时间间隔,可以是秒,分钟,小时,微妙,毫秒,纳秒
#include <chrono>
using nanoseconds = duration<long long, nano>; //纳秒 using microseconds = duration<long long, micro>; //微妙 using milliseconds = duration<long long, milli>; //毫秒 using seconds = duration<long long>; //秒 using minutes = duration<int, ratio<60>>; //分钟 using hours = duration<int, ratio<3600>>; //小时
时钟
system_clock : 获取时间点,需要转换为tim_t才能显示出来
steady_clock: 计时 类似秒数
high_resolution_clock: 高精度时钟
时钟类共用的一些函数
static time_point now() 获取当前时间点
static time64_t to_time_t(const time_point x);
time_point from_time_t(time_t x);
时间转换
duration_cast 不属于duration 类
浮点时长和整数时长之间可以直接隐式转化,其他情况要使用这个函数做转换
time_point_cast 不属于time_point
存在精度丢失的ratio转换,就必须使用time_point_cast做转换
C++随机数
种子序列:seed_seq
类似C语言srand函数 ,用来设置范围
size() 检测种子个数
generate()函数
param()函数
#include <iostream>
#include <random>
#include <functional>
#include <array>
using namespace std;
int main()
{
seed_seq seed = { 1,3,4,5,6,7,8 };
cout <<"size:" << seed.size() << endl;
array<int, 7> data;
seed.param(data.begin());
for (auto v : data)
{
cout << v << "\t";
}
cout << endl;
//array<int, 7> data2;
//seed.generate(data2.begin(), data2.end());
//for (auto v : data2)
//{
// cout << v << "\t";
//}
cout << endl;
return 0;
}
引擎适配器
shuffle_order_engine: 乱序随机数引擎适配器
independent_bits_engine: 独立位随机数引擎适配器
discard_block_engine: 丢弃块随机数引擎适配器
default_random_engine: 默认的随机数引擎
Cdefault_random_engine - C++ Reference
C++文件系统
filesystem简介: 函数 | Microsoft Docs
path类
file_status类
directory_entry类