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(){
	short num=10;
    cout<<sizeof(num)<<endl;
    return 0;
}
  • 实型(浮点型)
float (单精度) 4字节 7位
double(双精度)8字节 16位
类型字节位数
float47
double816
#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 \

  • 字符串型

    C 风格的字符串

    char str[]="helllo world"
    

    c++风格的字符串

    string str[]="hello"
    
  • bool 类型
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();
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐