C++初步学习
typora的使用黑马程序员讲义:视频基础核心提高数据类型整形整形字节大小取值范围int4字节(-231~231-1)short2字节(-215~215-1)long4字节(-231~231-1)long long8字节(-263~263-1)sizeof关键字sizeof读取变量的字节大小#include <iostream>using namepace std;int main()
黑马程序员讲义:
数据类型
整形 | 字节大小 | 取值范围 |
---|---|---|
int | 4字节 | (-231~231-1) |
short | 2字节 | (-215~215-1) |
long | 4字节 | (-231~231-1) |
long long | 8字节 | (-263~263-1) |
sizeof读取变量的字节大小
#include <iostream>
using namepace std;
int main(){
short num=10;
cout<<sizeof(num)<<endl;
return 0;
}
float (单精度) 4字节 7位
double(双精度)8字节 16位
类型 | 字节 | 位数 |
---|---|---|
float | 4 | 7 |
double | 8 | 16 |
#include <iostream>
using namepace std;
int main(){
float num=3.14f;
cout<<num<<endl;
float f1=3e2;//float f1=3e-1;
cout<<f1<<endl;
return 0;
}
char(字符) 1字节
#include <iostream>
using namepace std;
int main(){
char ch='b';
cout<<sizeof(ch)<<endl;
cout<<(int)(ch)<<endl;//输出ascll数字码
cout<<num<<endl;
return 0;
}
常见转义字符\n ? \t \
true --真 false --假
运算符
算数运算符 逻辑运算符 比较运算符 赋值运算符
b++与++b的区别
区别 | 解释 | b=3 |
---|---|---|
b++ | 后置递增 | 先运算再++ |
++b | 前置递增 | 先++再运算 |
程序流程结构
顺序结构 选择结构 循环结构
选择结构:if 与switch
循环结构:for 与while
break关键字跳出最近的循环
三目运算符:表达式1?表达式2:表达式3
做一个比较的:
c=(a>b?a:b);
选择条件switch (int or char):
switch (表达式)
case:(1){
break;
}
case:(2){
break;
}
default:{
break;
}
循环结构:
while()or for() or do{}while()
for(变量定义;判断条件;末尾循环体执行操作){
bool flag =false;
}
跳转结构:
break:break关键字跳出最近的循环
continue:执行循环到此结束,剩下的不执行
for(int i=0;i<=100;i++){
if(i%2=0){
continue;
}
cout<<i<<endl;
}
数组
一维数组:
数据类型 数组名 [长度];
数据类型 数组名 [长度]={x,x,x,x,};
数据类型 数组名 []={x,x,x,x,};
数组:存放在连续的内存空间;数据是相同的数据类型;
int arr[]={1,2,3,4}
arr[0]=1;arr[2]=3;
数组逆置
//数据1,2,3,4,5 输出5,4,3,2,1
int arr[]={1,2,3,4,5}
int start =0; int end =szieof(arr)/sizeof(arr[0])-1;
//start与end互换
for(start=0;start<end;end--){
int temp=arr[start];
int arr[start]=arr[end]
int arr[end]=temp
++start;
}
冒泡排序
//总共多少个数
for(int i=0;i<9;i++){
for(int j=0;j=9-1-i;j++){
if(arr[j]>arr[j+1])
swap(arr[j],arr[j+1])
/*
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
*/
}
}
二维数组
1.数据类型 数组名 [行数] [列数]
2.数据类型 数组名 [行数] [列数]={ {数据1,数据2},{数据3,数据4},…}
3.数据类型 数组名 [行数] [列数]={数据1,数据2,数据3,数据4,…}
4.数据类型 数组名 [ ] [列数]={数据1,数据2,数据3,数据4,…}
//int arr[0][0]=1;
int arr[2][2]={
{1,2},
{2,3}
}
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
cout<<arr[i][j]<<endl;
}
}
函数
1.返回值类型
2.函数名
3.参数列表
4.函数体语句
5.return 表达式
int add(int &a,int &b)
{
int sum=a+b;
return sum;
}
指针
指针:可以直接访问内存;指针保存地址;
int *p;int a=10;
int *p=&a;
//指针的形式
void swap1(int *x,int *y){
int z=*x;
*x=*y;
*y=z;
}
//引用的方式
void swap1(int &x,int &y){
int z=x;
x=y;
y=z;
}
int main{
swap1(&a,&b);
swap2(a,b);
}
指针的地址
int *p;//32位 4字节 64位8字节
空指针与野指针
空指针:指针变量指向内存为编号0的空间;
int *p=NIULL;
用途:初始化指针
野指针:非法的内存空间
int *p=(int *)0x1110;//指向非法地址
const 修饰指针
const 修指针 --常量指针
const int*p=&a;
//常量指针--指针的指向可以修改,但是指针的值不可以修改
//*p=20;// 指针不可以修改
p=&b;//指针指向地址可以修
指针常量:
int *const p=&a;
//指针常量--指针的指向不可以修改,但是指针的值可以修改
*p=20;// 指针不可以修改
//p=&b;//指针指向地址不可以修
const修饰指针又修饰地址
const int* const p;
//都不可以修饰
指针与数组
int arr[]={1,2};
int *p=arr;//arr就是一堆连续数组
cout<<*p<<endl;
p++;//第二个指针的地址
cout<<"访问第二个元素"<<*p<<endl;
指针与函数
形参传递不会修改值(值);实参传递会修改值(指针)
结构体
struct student{
string name;
int age;
int score;
}
结构体数组
struct 结构体名 数组名[元素个数]={{},{},{},{}}
struct student{
string name;
int age;
int score;
};
int main(){
struct stdent stuarr[3]={
{"张三",19,100},
{"张四",19,100}
{"张6",19,100}
};
//重新赋值
stuarr[2].name="ZHAO";
stuarr[2].age="80";
stuarr[2].score="100";
for(int i=0;i<3;i++){
cout<<stuarr[i].name
<<stuarr[i].age
<<stuarr[i].score<<endl;
}
}
结构体指针
struct student{
string name;
int age;
int score;
};
int main(){
struct student s={"张三",18,100};
student *p=&s;
p->age=10;
cout<<p->name
<<p->age
<<p->score<<endl;
}
C++核心编程
C++执行代码分为4个区
代码区:存放二进制的代码,由操作系统进行管理
全局区:存放全局变量与静态变量,常值区;
栈区:局部变量,形参。编译器自动分配释放
堆区:程序员自己创空间以及释放。new一个对象就要delete;
可执行文件执行程序之前分为:
全局区:
代码区共享的且只读的。
全局区存放全局变量与静态变量。常量区,字符串常量区以及其他常量存放在此;
全局区在程序结束后由系统释放
栈区:
局部变量,形参。编译器自动分配释放
注意事项:不返还局部变量的地址,栈区开辟的地址由编译器自动释放;
堆区:
程序员自己创空间以及释放。new一个对象就要delete;
new操作符
语法:new 数据类型;
引用
作用:给变量起别名
语法:数据类型 &变量=变量
引用必须初始化
引用一旦初始化后不可以更改。
引用做函数参数
作用:函数传参,可以利用引用的技术让形参修饰实参;
优点:可以简化修改实参;
#include <iostrem>
using namespace std
//传参
void swap1(int a,int b){
int temp =a;
a=b;
b=temp;
}
//引用
void swap2(int &a,int &b){
int temp =a;
a=b;
b=temp;
}
//指针
void swap2(int *a,int *b){
int temp =*a;
*a=*b;
*b=temp;
}
imt main(){
int a=1;int b=0;
swap1(a.b)
}
引用返回值
int & test1(){
int a=10;return a;
}
int&test2(){
static int a=0;//静态变量存放全局区
return a;
}
int main(){
//int &ref=test1();//错误的,不能把局部变量返回
int &Ref=test2();//正确使用
test2()=100;//函数可以等号左值;返回值是引用可以作为左值
cout<<Ref<<endl;//输出100
}
函数重载
函数重载满足条件:
参数不一样
同一作用域
数目不一样
修饰类型不一样
void fun(int &a){
cout<<"fun"<<a<<endl;
}
void fun(const int &a){
cout<<"fun const"<<a<<endl;
}
int main(){
int a=10;
fun(a);
//结果:
//fun const 10;
fun(10);
//结果:
//fun const 10;
return 0;
}
//结果:
//fun const 10;
加号移运算符:
#include <iostream>
using namespace std;
class Person{
public:
//成员函数重载
/*
Person operator+(Person &p){
Person temp;
temp.m_A=this->m_A+p.m_A;
temp.m_B=this->m_B+p.m_B;
return temp;
}
*/
public:
int m_A=10;
int m_B=10;
};
//全局函数重载
Person operator+(Person &p1,Person &p2){
Person temp;
temp.m_A=p1.m_A+p2.m_A;
temp.m_B=p1.m_B+p2.m_B;
return temp;
}
/*
void test01(void){
Person p1;
p1.m_A=10;
p1.m_B=10;
Person p2;
p2.m_A=10;
p2.m_B=10;
Person p3=p1+p2;//=Person p3 =p1.operator+(p2);
cout<<"p3.m_A "<<p3.m_A<<" p3.m_B "<<p3.m_B<<endl;
}
*/
void test02(void){
Person p1;
p1.m_A=10;
p1.m_B=10;
Person p2;
p2.m_A=10;
p2.m_B=10;
Person p3=p1+p2;//=Person p3 =p1.operator+(p2);
cout<<"p3.m_A "<<p3.m_A<<" p3.m_B "<<p3.m_B<<endl;
}
int main()
{
cout << "Hello World"<<endl;
test02();
return 0;
}
成员函数的本质:p1.operator+(p2);
全局函数本质的调用:operator+(p1,p2)
左移运算符
#include <iostream>
using namespace std;
class Person{
public:
int m_A=10;
int m_B=10;
};
ostream &operator<<(ostream &cout,Person &p){
cout<<"m_A="<<p.m_A<<"m_B="<<p.m_B;
return cout;
}
void test(){
Person p;
p.m_A=10;
p.m_B=10;
cout<<p<<endl;
}
int main()
{
test();
return 0;
}
递增运算符
#include <iostream>
using namespace std;
class MyInteger{
friend ostream& operator<<(ostream&cout,MyInteger myint);
public:
MyInteger(){
m_Num=0;
}
//重载前置运算符
MyInteger& operator++(){
++m_Num;
return *this;
}
//int 占位操作
//后置返回的值
MyInteger operator++(int){
//先记录结果
MyInteger temp=*this;
//后递增
m_Num++;
//最后将结果返回
return temp;
}
private:
int m_Num;
};
//重载<<运算符
ostream& operator<<(ostream&cout,MyInteger myint)
{
cout<<myint.m_Num;
return cout;
}
void test(){
MyInteger myint;
cout<<++myint<<endl;
}
void test2(){
MyInteger myint;
cout<<myint++<<endl;
cout<<myint<<endl;
}
int main()
{
// int a;
// cout <<++a<<endl;//a=11
// cout <<a++<<endl;//a=10
test2();
return 0;
}
赋值运算符
C++提供析构函数、构造函数、拷贝函数、赋值运算符operator=
#include <iostream>
using namespace std;
class Person{
public:
Person (int age){
m_age=new int(age);
cout<<"m_age="<<*m_age<<endl;
}
~Person (){
delete m_age;
}
Person& operator=(Person &p){
if(m_age!=0){
delete m_age;
m_age=0;
}
m_age=new int (*p.m_age);
return *this;
}
private:
int *m_age;
};
void test(){
Person p(18);
Person p2(28);
Person p3(98);
p3=p=p2;
}
int main()
{
test();
return 0;
}
关系运算符号重载
#include <iostream>
using namespace std;
class Person{
public:
Person (string name,int age){
m_name=name;
m_age=age;
};
bool operator==(Person &p)
{
if(this->m_name==p.m_name && this->m_age==m_age)
return true;
else
return false;
}
private:
int m_age;
string m_name;
};
void test(){
Person p1("a",1);
Person p2("a",1);
p1=p2;
if(p1==p2)
cout<<"相等的"<<endl;
}
int main()
{
test();
return 0;
}
函数调用运算符
#include <iostream>
#include <string>
using namespace std;
class Person{
public:
void operator ()(string m_name){
cout<<m_name<<endl;
}
private:
int m_age;
string m_name;
};
class add{
public:
int operator ()(int num1,int num2){
return num1+num2;
}
};
void test(){
Person p1;
p1("1");
add Add;
int ret =Add(1,1);
cout<<ret<<endl;
}
int main()
{
test();
return 0;
}
继承
继承的父类与子类
父类=基类
子类=派生类
#include <iostream>
#include <string>
using namespace std;
class BasePage{
public:
void header(){
cout<<"C++"<<endl;
}
void page(){
cout<<"d++"<<endl;
}
};
class A:public BasePage{
public:
void content(){
cout<<"学习视频"<<endl;
}
};
class py:public BasePage{
public:
void content(){
cout<<"py学习视频"<<endl;
}
};
void test(){
py b;
b.content();
}
int main()
{
test();
return 0;
}
继承方式
class 子类:继承方式 父类
继承方式:
公共继承
私有继承
受保护继承
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公共继承
class Son1 :public Base1
{
public:
void func()
{
m_A; //可访问 public权限
m_B; //可访问 protected权限
//m_C; //不可访问
}
};
void myClass()
{
Son1 s1;
s1.m_A; //其他类只能访问到公共权限
}
//保护继承
class Base2
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son2:protected Base2
{
public:
void func()
{
m_A; //可访问 protected权限
m_B; //可访问 protected权限
//m_C; //不可访问
}
};
void myClass2()
{
Son2 s;
//s.m_A; //不可访问
}
//私有继承
class Base3
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son3:private Base3
{
public:
void func()
{
m_A; //可访问 private权限
m_B; //可访问 private权限
//m_C; //不可访问
}
};
class GrandSon3 :public Son3
{
public:
void func()
{
//Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到
//m_A;
//m_B;
//m_C;
}
};
继承中的对象模型
在父类中,那些成员属于子类对象
#include <iostream>
#include <string>
using namespace std;
class Base1{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class son:public Base1{
public:
int m_D;
//私有变量是可以继承但是不可访问
};
void test(){
cout<<sizeof(son)<<endl;
}
int main()
{
test();
return 0;
}
继承的析构与构造顺序
问题;父类和子类的构造对象与析构对象是先调用谁的?
答:构造函数先调用父类然后子类,析构函数是先调用子类然后父类
#include <iostream>
#include <string>
using namespace std;
class Base1{
public:
Base1(){
cout<<"base1构造函数"<<endl;
}
~Base1(){
cout<<"base1析构函数"<<endl;
}
};
class son:public Base1{
public:
son(){
cout<<"son1构造函数"<<endl;
}
~son(){
cout<<"son析构函数"<<endl;
}
};
void test(){
son s;
}
int main()
{
test();
return 0;
}
同名静态函数处理
静态函数继承过程中是会隐藏的,要调用父类的静态成员需要加上作用域。
#include <iostream>
#include <string>
using namespace std;
class Base1{
public:
static int m_A;
static void func(){
cout<<"base- stact"<<endl;
}
};
int Base1::m_A=10;
class son:public Base1{
public:
static int m_A;
static void func(){
cout<<"son- stact"<<endl;
}
};
int son::m_A=20;
void test(){
//通过对象访问数据
son s;
cout<<s.m_A<<endl;
//通过类名访问
cout<<Base1::m_A<<endl;
//通过子类访问父类
cout<<son::Base1::m_A<<endl;
//------------------//
s.func();
s.Base1::func();
}
int main()
{
test();
return 0;
}
多继承语法
语法:class 子类:继承方式 父类1.继承方式 父类2
多继承可能会引发同名函数,需要加作用域加以区分
c++内不建议使用多继承
菱形继承
两个派生类继承一个基类,又有某个类继承两个派生类
会产生二义性
#include <iostream>
using namespace std;
class Animal{
public:
int m_Age;
};
//虚继承--虚基类
//共享基类成员变量
//继承的
//vbptr虚基类指针指向vbtable(虚基类表)通过偏移量指向基类的成员变量指针
//解决了二义性问题,共有一个成员变量
//虚继承实际上继承了两个虚指针
class sheep:virtual public Animal{
};
class tuo:virtual public Animal{
};
class sheeptuo:public tuo,public sheep{
};
void test(){
sheeptuo st;
st.sheep::m_Age=2;
st.tuo::m_Age=20;
cout<<st.sheep::m_Age<<endl;
cout<<st.tuo::m_Age<<endl;
//数据只有一个m_Age
cout<<st.m_Age<<endl;
}
int main()
{
cout << "Hello World"<<endl;
test();
return 0;
}
多态
静态多态
动态的多态
区别:
静态地址在编译时已经确定了函数地址;
动态在运行阶段确定地址;
#include <iostream>
using namespace std;
class Animal{
public:
int m_Age;
public:
virtual void speak(){
cout<<"动物在说话"<<endl;
}
};
//没有virtual
//编译阶段绑定了地址
//有virtual
//运行阶段绑定了地址,地址晚绑定
//虚函数只有虚函数表,只能通过虚指针偏移指向运算
class cat: public Animal{
public:
void speak(){
cout<<"猫在说话"<<endl;
}
};
class dog:public Animal{
public:
void speak(){
cout<<"狗在说话"<<endl;
}
};
void dospeak(Animal &animal){
animal.speak();
}
void test(){
cat Cat;
dospeak(Cat);
dog Dog;
dospeak(Dog);
}
int main()
{
cout << "Hello World"<<endl;
test();
return 0;
}
#include <iostream>
using namespace std;
class Animal{
public:
int m_Age;
public:
virtual void speak(){
cout<<"动物在说话"<<endl;
}
};
//没有virtual
//编译阶段绑定了地址
//有virtual
//运行阶段绑定了地址,地址晚绑定
//虚函数只有虚函数表,只能通过虚指针偏移指向运算
//子类重写父类虚函数
//所有的虚函数是将虚的函数或者变量存放在虚函数表中,在通过虚函数表偏移指针地址指向实函数指向。
//例如:
//1.父类虚函数--子类调用时刻会调用父类虚函数,然后父类虚函数存放在虚函数表,将父类虚函数表指针偏向子类实函数
//2.子类虚函数,先调用父类子函数变量,子类虚函数会产生指针偏移指向父类实函数,此刻是公有一个父类实变量
class cat: public Animal{
public:
void speak(){
cout<<"猫在说话"<<endl;
}
};
class dog:public Animal{
public:
void speak(){
cout<<"狗在说话"<<endl;
}
};
void dospeak(Animal &animal){
animal.speak();
}
void test(){
cat Cat;
dospeak(Cat);
dog Dog;
dospeak(Dog);
}
int main()
{
cout << "Hello World"<<endl;
test();
return 0;
}
多态原理剖析
父类的虚函数
virtual void speak(){
cout<<"动物在说话"<<endl;
}
/*
vfptr -虚函数表指针
存放的虚函数表
表内记录着虚函数地址
地址:Animal::speak
*/
虚函数里面只有一个虚函数表,发生重写时候,先调用父类,发现是虚函数,然后就会调用重写后的实函数
否则是实函数就会直接显示父类实函数的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQDWpOs5-1618992508188)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210417110433995.png)]
纯虚函数与抽象类
virtual 返回值 函数名 (参数列表)=0
#include <iostream>
using namespace std;
//纯虚函数
class Base{
public:
virtual void fun()=0;//纯虚函数,这个类称为抽象类
//1.无法实例化抽象
//例如:无法 Base::fun();
//new也不行
/*
2.抽象类的子类必须重写父类的纯虚函数,否则也属于抽象类
就不能创建对象
*/
};
class son:public Base{
public:
virtual void fun(){
cout<<"a"<<endl;
}//纯虚函数,这个类称为抽象类
};
int main()
{
son s;
cout << "Hello World";
return 0;
}
虚析构与纯析构
多态使用,如果子类中属性开辟到堆区,那么父类指针在释放时无法调用子类析构代码
解决办法将父类的析构函数改为虚析构与纯虚析构
#include <iostream>
#include <string>
using namespace std;
class Base{
public:
virtual void speak()=0;
Base(){
cout<<"base构造中"<<endl;
}
virtual ~Base(){
cout<<"bas析构中"<<endl;
}
// virtual ~Base=0; 纯虚析构 但是会报错。需要代码实现 因为父类已经创建了堆区
};
class son:public Base{
public:
son(string name)
{
m_Name=new string(name);
cout<<"son构造中"<<endl;
}
~son()
{
delete m_Name;
cout<<"son析构中"<<endl;
m_Name=0;
}
virtual void speak(){
cout<<"小猫在说话"<<endl;
}
string *m_Name;
};
int main()
{
Base *base =new son("a");
base->speak();
delete base;//这里会析构不来子类堆区,要去父类析构使用虚析构
cout << "Hello World";
return 0;
}
模板
模板的概念
模板就是建立通用的模具,大大提高复用性
模板的特点:
- 模板不可以直接使用,它只是一个框架
- 模板的通用并不是万能的
函数模板
- C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板
- C++提供两种模板机制:函数模板和类模板
函数模板语法
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
语法:
template<typename T>
函数声明或定义
12
解释:
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
示例:
//交换整型函数
void swapInt(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
//交换浮点型函数
void swapDouble(double& a, double& b) {
double temp = a;
a = b;
b = temp;
}
//利用模板提供通用的交换函数
template<typename T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
//swapInt(a, b);
//利用模板实现交换
//1、自动类型推导
mySwap(a, b);
//2、显示指定类型
mySwap<int>(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main() {
test01();
system("pause");
return 0;
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
总结:
- 函数模板利用关键字 template
- 使用函数模板有两种方式:自动类型推导、显示指定类型
- 模板的目的是为了提高复用性,将类型参数化
注意事项:
- 自动类型推导,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定出T的数据类型,才可以使用
示例:
//利用模板提供通用的交换函数
template<class T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
// 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
mySwap(a, b); // 正确,可以推导出一致的T
//mySwap(a, c); // 错误,推导不出一致的T类型
}
// 2、模板必须要确定出T的数据类型,才可以使用
template<class T>
void func()
{
cout << "func 调用" << endl;
}
void test02()
{
//func(); //错误,模板不能独立使用,必须确定出T的类型
func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}
int main() {
test01();
test02();
system("pause");
return 0;
}
总结:
- 使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型
函数模板案例
案例描述:
- 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
- 排序规则从大到小,排序算法为选择排序
- 分别利用char数组和int数组进行测试
示例:
//交换的函数模板
template<typename T>
void mySwap(T &a, T&b)
{
T temp = a;
a = b;
b = temp;
}
template<class T> // 也可以替换成typename
//利用选择排序,进行对数组从大到小的排序
void mySort(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
int max = i; //最大数的下标
for (int j = i + 1; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i) //如果最大数的下标不是i,交换两者
{
mySwap(arr[max], arr[i]);
}
}
}
template<typename T>
void printArray(T arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
//测试char数组
char charArr[] = "bdcfeagh";
int num = sizeof(charArr) / sizeof(char);
mySort(charArr, num);
printArray(charArr, num);
}
void test02()
{
//测试int数组
int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };
int num = sizeof(intArr) / sizeof(int);
mySort(intArr, num);
printArray(intArr, num);
}
int main() {
test01();
test02();
system("pause");
return 0;
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
总结:模板可以提高代码复用,需要熟练掌握
#include <iostream>
using namespace std;
template <typename T>
void myswap(T &a,T &b){
T temp=a;
a=b;
b=temp;
};
template <typename T>
void mysort(T arr[],int len){
for(int i=0;i<len;i++){
int max =i;//认定最大值下标
for(int j=i+1;j<len;j++){
// 认定的最大值比比遍历的最大值要小,说名j的真正最大值
if(arr[max]<arr[j])
max=j;
}
if(max!=i)//一开始的最大值不相等就相互交换
myswap(arr[max],arr[i]);
}
};
template <class T>
void printfarr(T arr[],int len){
for (int i=0;i<len;i++){
cout<<arr[i];
}
cout<<endl;
}
void test01(){
char arr[]="abcde";
int num=sizeof(arr)/sizeof(char);
mysort(arr,num);
printfarr(arr,num);
};
void test02(){
int intarr[]={7,8,1,3,9,2,4,6};
int intnum=sizeof(intarr)/sizeof(int);
mysort(intarr,intnum);
printfarr(intarr,intnum);
};
int main()
{
test02();
return 0;
}
普通函数与函数模板区别
普通函数发生类型转换
函数模板不会发生隐类型转换
显示指定函数模板会发生隐类型转换
普通函数与函数模板的调用规则
#include <iostream>
using namespace std;
//1.函数模板都可以调用,先调用普通函数
//2.可以通过空模板参数列表 强制调用函数模板
//3.函数模板可以发生重载
//4.如果函数模板可以产生更好的匹配,有限调用函数模板
void myprin(int a, int b){
cout<<"myprint"<<endl;
};
template <class T>
void myprin(T a,T b){
cout<<"函数模板"<<endl;
};
void test(){
int a=1,b=2;
myprin<>(a,b);//空模板
//myprin(a,b);
};
int main() {
test();
return 0;
}
模板的局限性
参数在数组或者其他类型无法比较
类模板
#include <iostream>
#include <string>
using namespace std;
template <class T_num,class T_name>
class Person{
public:
Person(T_num num,T_name name){
this->m_Name=name;
this->m_num=num;
}
T_name m_Name;
T_num m_num;
void show(){
cout<<"m_Name="<<m_Name<<endl;
cout<<"m_num="<<m_num<<endl;
}
};
void test(void){
Person<int,string>p1(1,"qq");
p1.show();
}
int main() {
test();
return 0;
}
类模板与函数模板的区别
1.类模板没有自动推导参数类型
2.类模板在参数列表有默认参数列表
类模板中的成员函数创建时机
1.普通函数在一开始就创建了成员函数
2.类模板成员函数在调用时刻才开始创建
#include <iostream>
#include <string>
using namespace std;
class Person1{
public:
void show1(){
cout<<"Person1 show()="<<endl;
}
};
class Person2{
public:
void show2(){
cout<<"Person2 show()="<<endl;
}
};
template <class T>
class maclass{
public:
T obj;
void fun1(){
obj.show1();
}
void fun2(){
obj.show2();
}
};
void test(void){
maclass<Person1>m;
m.fun1();
}
int main() {
test();
cout<<"a"<<endl;
return 0;
}
类模板对象做函数参数
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Person{
public:
Person(T1 name,T2 num){
this->m_Name=name;
this->m_num=num;
}
void show1(){
cout<<this->m_Name<<endl;
cout<<this->m_num<<endl;
}
T1 m_Name;
T2 m_num;
};
void test(void){
Person<string,int>m("aaa",2);
m.show1();
}
template <class T1,class T2>
void printperson2(Person<T1,T2>&P){
P.show1();
}
void test02(void){
Person<string,int>m("bbb",3);
printperson2(m);
}
int main() {
test02();
cout<<"a"<<endl;
return 0;
}
类模板与继承
#include <iostream>
using namespace std;
template<class T>
class Student {
public:
T m_age;
T m_score;
};
//类模板的继承方式
class son:public Student<int>{
public:
};
template <class Ts,class T2>//Ts给son2 T2给STUDENT
class son2:public Student<T2>{
public:
Ts m_Name;
void show(){
cout<<"'a"<<endl;
}
};
int main()
{
son2<int,char>S2;
S2.show();
return 0;
}
分文件编写
1.直接包含CPP文件
2.将声明和实现写在同一个文件中,并且改名hpp
STL标准模板库
c++面向对象与泛型编程
容器、算法、迭代器、仿函数、适配器、空间配置器
容器和算法之间通过迭代器进行无缝连接
容器:各种数据结构vector,list,deque,set,map.
算法:各种常见的算法。sort,find,copy,for_each;
迭代器:连接算法与容器之间
仿函数:类似函数,算法的策略
适配器:一种用来修饰容器与仿函数的接口
空间配置器:负责空间的配置与管理
容器:序列容器与关联式容器
序列容器:强调值得排序,容器内有固定位置
关联式容器:二叉树结构,各元素之间没有严格的物理顺序意义
迭代器初始
容器:vector
算法:for_each
迭代器vector::iterator
#include <iostream>
#include<vector>
using namespace std;
void test01(){
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
vector<int>::iterator itBegin=v.begin();
vector<int>::iterator itEnd=v.end();
for(itBegin;itBegin!=itEnd;++itBegin)
{
cout<<*itBegin<<endl;
}
};
int main()
{
test01();
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32DyPPsR-1618992508191)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210419110055234.png)]
- 侯捷 STL源码解析
#include <iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
//vector存放自定义数据类型
class Person{
public:
int m_age;
string m_name;
public:
Person(string name,int age){
this->m_age=age;
this->m_name=name;
}
};
void test01(){
vector<Person> v;
Person p1("a",1);
Person p2("b",2);
Person p3("c",3);
Person p5("d",4);
Person p6("e",5);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p5);
v.push_back(p6);
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
cout << "Name:" << (*it).m_name << " Age:" << (*it).m_age << endl;
}
};
int main()
{
test01();
cout<<"endl"<<endl;
return 0;
}
sting容器
vector 单端数组
vector与数组区别在于vector可以动态扩展
前端会封闭,后端会提供尾插,插入
vector创建容器
vector v; //采用模板返回类实现
vector(v.begin(),v.end())//将v.begin(),v.end())区间的元素赋值为本身
vector(n,elem)//构造函数将n个elem拷贝本身
vector(const vector &vec)//拷贝构造函数
#include <iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
void test01(){
vector<int> v;
for (int i=0;i<10;i++) {
v.push_back(i);
}
for (auto it=v.begin();it!=v.end();it++) {
cout<<*it<<" ";
cout<<"正在打印..."<<endl;
cout<<*it<<endl;
}
cout<<endl;
};
int main()
{
test01();
cout<<"endl"<<endl;
return 0;
}
vector操作
push_back();尾部插入
pop_back();删除最后一个
insert插入
erase擦除
数据存取
at()
queue容器
先进先出(进push)(pop出)
只有队头与队位才能被使用数据
#include<iostream>
using namespace std;
#include<queue>
class Person{
public:
string m_Name;
int m_Age
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
}
void test(){
queue<Person> q;
Person p1("a",1);
Person p2("b",2);
Person p3("c",3);
Person p4("d",4);
Person p5("e",5);
q.push(p1);q.push(p2);
q.push(p3);q.push(p4);q.push(p5);
}
list链表
功能:将数据非连续存储,可以对任意位置进行插入或者删除
数组是连续存储
容器遍历速度没有数组快,占用的空间要比数组大
stl的双向循环表
list构造函数
listlst
list<beg,len>
#include<iostream>
using namespace std;
#include<queue>
#include<list>
class Person{
public:
string m_Name;
int m_Age;
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
};
void test(){
list<int> lst;
lst.push_back(10);
lst.push_back(10);
lst.push_back(30);
lst.push_back(10);
for(auto it=lst.begin();it!=lst.end();it++){
cout<<*it<<endl;
}
}
int main()
{
test();
}
list容器赋值与交换
#include<iostream>
using namespace std;
#include<queue>
#include<list>
class Person{
public:
string m_Name;
int m_Age;
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
};
void test(){
list<int> lst;
lst.push_back(10);
lst.push_back(10);
lst.push_back(30);
lst.push_back(10);
for(auto it=lst.begin();it!=lst.end();it++){
cout<<*it<<endl;
}
list<int> lst2;
lst2=lst;
for(auto it=lst2.begin();it!=lst2.end();it++){
cout<<*it<<endl;
}
list<int> lst3;
lst3.assign(lst2.begin(),lst2.end());
for(auto it=lst3.begin();it!=lst3.end();it++){
cout<<*it<<endl;
}
}
int main()
{
test();
}
list容器的大小操作
#include<iostream>
using namespace std;
#include<queue>
#include<list>
void pf(auto &v){
for(auto itself=v.begin();itself!=v.end();itself++){
cout<<*itself<<endl;
}
}
void test(){
list<int> lst;
lst.push_back(10);
lst.push_back(10);
lst.push_back(30);
lst.push_back(10);
auto it =lst.begin();
lst.insert(++it,80);
pf(lst);
it =lst.begin();
lst.erase(++it);
pf(lst);
}
int main()
{
test();
}
pair对组
#include<iostream>
using namespace std;
#include<string>
#include<set>
void test(){
pair<string,int>m(string("a"),10);
cout<<m.first<<m.second<<endl;
pair<string,int>b=make_pair("b",8);
cout<<b.first<<b.second<<endl;
};
int main()
{
test();
}
map容器
map所有的元素都是pair
pair第一个是key,第二个是value
所有 元素都会根据元素的键值排序
map不允许key重复
map<T1,T2>mp
#include<iostream>
#include<string>
#include<map>
using namespace std;
void printmap(auto &m){
for(auto it=m.begin();it!=m.end();it++){
cout<<"key"<<(*it).first<<"value"<<(*it).second<<endl;
}
}
void test(){
map<int, string>m;
m.insert(pair<int,string>(1,"aa"));
m.insert(pair<int,string>(2,"ba"));
printmap(m);
};
int main()
{
test();
}
更多推荐
所有评论(0)