一、多态
多态的满足条件:(1)有继承关系。(2)子类重写父类的虚函数。
多态的使用条件:父类指针或引用指向子类对象。
重写:函数返回值类型、函数名、参数列表完全一致。
多态的原理:父类使用virtual后,会在内部有一个虚函数表指针,指向父类中的虚函数;而子类与父类发生多态后子类中也有一个虚函数指针指向自己的虚函数,所以会发生晚绑定。
(1)父类引用指向子类对象。
# include <iostream>
using namespace std;
//多态的满足条件:(1)有继承关系。(2)子类重写父类的虚函数。
class student
{
public:
//如果不写virtual就会是地址早绑定,写student类就绑定地址
//如果写virtual就是地址晚绑定,指向后面的子类地址
virtual void speak ()
{
cout<<"student is speaking"<<endl;
}
};
class stu1:public student
{
public:
void speak()//重写:函数返回值类型、函数名、参数列表完全一致。
{
cout<<"stu1 is speaking"<<endl;
}
};
class stu2:public student
{
public:
void speak()
{
cout<<"stu2 is speaking"<<endl;
}
};
void test01(student&stu)//多态的使用条件:父类指针或引用指向子类对象。
{
stu.speak();
}
int main(int argc, char const *argv[])
{
stu1 stu;
test01(stu);
return 0;
}
(2)父类指针指向子类对象。
# include <iostream>
using namespace std;
class yunsuan
{
public:
int num1;
int num2;
virtual int suan()
{
return 0;
}
};
class addsuan:public yunsuan
{
public:
virtual int suan()
{
return num1+num2;
}
};
class jiansuan:public yunsuan
{
public:
virtual int suan()
{
return num1-num2;
}
};
void test01()
{
yunsuan* shu=new addsuan;//父类指针指向子类对象
shu->num1=100;
shu->num2=100;
cout<<shu->num1<<"+"<<shu->num2<<"="<<shu->suan()<<endl;
delete shu;
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}
二、纯虚数
# include <iostream>
using namespace std;
class student//抽象类,不能定义对象
{
public:
virtual void function()=0;//纯虚数
};
class stu1:public student
{
public:
virtual void function()
{
cout<<"stu1 function"<<endl;
}
};
void f()
{
//student stu2;//不允许使用抽象类类型 "student" 的对象
student* stu=new stu1;
stu->function();
}
int main(int argc, char const *argv[])
{
f();
return 0;
}
三、虚析构函数和纯虚析构函数
# include <iostream>
# include <string>
using namespace std;
class student
{
public:
student()
{
cout<<"student gouzao function"<<endl;
}
virtual ~student()//虚析构函数
{
cout<<"student xigou function"<<endl;
}
//这种形式会使子类无法调用自己的析构函数
// ~student()
// {
// cout<<"student xigou function"<<endl;
// }
/*
纯虚析构函数必须要有实现,因为如果没有实现会使父类中的堆区无法释放
有了纯虚函数后类也为抽象类,无法实现实例化对象
*/
//virtual ~student()=0;
};
//纯虚析构函数需要在外部定义
// student::~student()
// {
// cout<<"student xigou function"<<endl;
// }
class stu1:public student
{
public:
stu1(string name)
{
str=new string(name);
cout<<"stu1 gouzao function"<<endl;
}
virtual ~stu1()
{
cout<<"stu1 xigou function"<<endl;
if(str!=NULL)
{
delete str;
str=NULL;
}
}
string* str;
};
void test01()
{
student* stu2=new stu1("zhang");
delete stu2;
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}
四、函数模板
# include <iostream>
using namespace std;
template<typename T>//模板,T可以代表任意类型,函数模板用typename,类模板用class
void my_swap(T& a,T& b)
{
T temp=a;
a=b;
b=temp;
}
template<class T>//注意事项下面
void f()
{
cout<<"****"<<endl;
}
void test02()
{
//f();//错误,没有指定模板T的类型,因为函数中也没有用到T,所以编译器无法推断出类型
f<int>();//正确,直接指定T的类型
}
void test01()
{
int a=10;
int b=20;
//第一种:自动类型推导,但必须满足两个数据类型一致
my_swap(a,b);
//第二种:指定T的数据类型
//swap<int>(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;
}
int main(int argc, char const *argv[])
{
test01();
test02();
return 0;
}
五、普通函数与函数模板的调用规则
1.如果普通函数和函数模板都可以实现,优先调用普通函数。
2.可以通过空模板参数列表来强制调用函数模板。
3.函数模板也可以发生重载。
4.如果函数模板可以产生更好的匹配,则优先调用函数模板。
# include <iostream>
using namespace std;
void myprint(int a,int b)
{
cout<<"普通函数调用"<<endl;
}
template<typename T>
void myprint(T a,T b)
{
cout<<"模板函数调用"<<endl;
}
template<typename T>//函数模板也可以发生重载
void myprint(T a,T b,T c)
{
cout<<"重载模板函数调用"<<endl;
}
void test01()//如果普通函数和函数模板都可以实现,优先调用普通函数
{
int a=10;
int b=10;
myprint(a,b);
}
void test02()//可以通过空模板参数列表来强制调用函数模板
{
int a=10;
int b=10;
myprint<>(a,b);
}
void test03()//如果函数模板可以产生更好的匹配,则优先调用函数模板
{
char a='a';
char b='c';
myprint(a,b);
}
void test04()
{
int a=0,b=0,c=0;
myprint(a,b,c);
}
int main(int argc, char const *argv[])
{
test01();
test02();
test03();
test04();
return 0;
}
六、比较自定义数据类型的另一种方法
# include <iostream>
# include <string>
using namespace std;
class student
{
public:
student(string m_name,int m_age)
{
this->age=age;
this->name=name;
}
string name;
int age;
};
template<typename T>
bool compare(T a,T b)
{
if (a==b)
{
return true;
}
else
{
return false;
}
}
//重载上上面的比较函数,注意参数列表中的类型必须一致,要么都是引用,要么都不是。
template<>bool compare(student stu1,student stu2)
{
if (stu1.age==stu2.age&&stu1.name==stu2.name)
{
return true;
}
else
{
return false;
}
}
void test01()
{
student stu1("aaa",20);
student stu2("bbb",20);
bool result=compare(stu1,stu2);
if (result)
{
cout<<"stu1=stu2"<<endl;
}
else
{
cout<<"stu1!=stu2"<<endl;
}
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}
七、类模板的基本语法
# include <iostream>
# include <string>
using namespace std;
template<class nametype,class agetype=int>//类模板在参数列表中可以有默认参数
class student
{
public:
student(nametype m_name,agetype vm_age)
{
name=m_name;
age=m_age;
}
nametype name;
agetype age;
};
void test01()
{
//student stu("zhang",19);//错误,类模板没有自动类型推导
student<string,int> stu("zhang",19);
//student<string> stu("zhang",19);类模板在参数列表中可以有默认参数
cout<<"name="<<stu.name<<" "<<"age="<<stu.age<<endl;
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}
八、类模板对象作为函数参数
# include <iostream>
# include <string>
using namespace std;
template<class T1,class T2>
class student
{
public:
student(T1 name,T2 age)
{
m_age=age;
m_name=name;
}
T1 m_name;
T2 m_age;
};
void function(student<string,int>& stu)
{
cout<<stu.m_name<<" "<<stu.m_age<<endl;
}
int main(int argc, char const *argv[])
{
student<string,int> stu1("zhang",19);
function(stu1);
return 0;
}
九、子类继承模板的父类
# include <iostream>
# include <string>
using namespace std;
template<class T>
class student
{
public:
T a;
};
//第一种方法
class student1:public student<int>//直接指定父类中T为int
{
};
void test01()
{
student1 stu1;
}
//第二种方法
template<class T1,class T2>
class student2:public student<T1>//可以动态的指定父类中的T的类型
{
};
void test02()
{
student2<int,string> stu2;
}
int main(int argc, char const *argv[])
{
return 0;
}
十、类模板成员函数类外实现
# include <iostream>
# include <string>
using namespace std;
template<class T1,class T2>
class student
{
public:
student(T1 name,T2 age);
void print();
T2 m_age;
T1 m_name;
};
template<class T1,class T2>//构造函数类外实现
student<T1,T2>::student(T1 name,T2 age)
{
m_name=name;
m_age=age;
}
template<class T1,class T2>//成员函数类外实现
void student<T1,T2>::print()
{
cout<<m_age<<m_name<<endl;
}
void test01()
{
student<string,int> stu("zhang",19);
stu.print();
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}