
【C++】多态和模版
AI-摘要
Tianli GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
多态和模版
1. 多态
1.1 基本概述
多态的作用
- 拓宽函数的参数范围,函数所需参数为父类类型,提供给当前函数的实际参数,可以是父类本身对象,或者其子类对象
- 拓宽函数的返回值范围,函数返回值类型为父类类型,提供给返回值使用的实际类型,可以是父类本身,也可以是子类对象。
1.2 多态案例
#include <iostream>
using namespace std;
/*
以下案例就是一个多态案例,多态有一定的要求
1. 父类中的成员函数,子类进行重写
2. 父类中的成员函数可以是虚函数或者纯虚函数,父类成员函数
可以作为函数执行兜底或者规则/规范
*/
class Animal
{
public:
virtual void eat() = 0;
};
class Dog : public Animal
{
public:
void eat() { cout << "狗吃饭!" << endl; }
};
class HSQ : public Dog
{
public:
void eat() { cout << "哈士奇撕家!" << endl; }
};
class Cat : public Animal
{
public:
void eat() { cout << "猫吃饭!" << endl; }
};
class Panda : public Animal
{
public:
void eat() { cout << "熊猫吃竹子!" << endl; }
};
/**
* 喂养函数,提供给函数的参数是动物类型引用
*
* @param ani 动物类型引用,可以提供的实际参数为动物对象本身,或者其后代对象
*/
void feed_animal(Animal &ani);
int main(int argc, char const *argv[])
{
Dog dog;
Panda panda;
HSQ hsq;
Cat cat;
feed_animal(dog);
feed_animal(panda);
feed_animal(hsq);
feed_animal(cat);
return 0;
}
void feed_animal(Animal &ani)
{
ani.eat();
}
2. 模版
2.1 模版引入
回顾
重写 Override
- 必须存在必要的继承关系
- 子类重写父类函数
- 子类重写父类函数声明完全一致
- 函数对应的函数体内容根据当前子类所需,进行特征化实现
重载 Reload
- 在同一个区域内容,例如,同一个文件,同一个类,同一个结构体,同一个命名空间
- 函数名必须一致
- 函数的形式参数列表必须不一致
- 函数的返回值类型不作为重载区分依据
#include <iostream>
using namespace std;
// #define NORMAL
#define TEMPLATE
/*
利用函数重载机制,完成当前函数,函数对应的名称一致,形式参数
列表数据类型不一致
*/
#ifdef NORMAL
void show(int n);
void show(float n);
void show(double n);
void show(string str);
void show(char c);
#endif
#ifdef TEMPLATE
template <typename T>
void show(T t);
#endif
int main(int argc, char const *argv[])
{
// void show<int>(int t)
show(10);
// void show<double>(double t)
show(10.5);
// void show<float>(float t)
show(10.5F);
// void show<const char *>(const char *t)
show("脸疼!");
// void show<char>(char t)
show('A');
return 0;
}
#ifdef TEMPLATE
template <typename T>
void show(T t)
{
cout << "Template Function Value : " << t << endl;
}
#endif
#ifdef NORMAL
void show(int n)
{
cout << "Value : " << n << endl;
}
void show(float n)
{
cout << "Value : " << n << endl;
}
void show(double n)
{
cout << "Value : " << n << endl;
}
void show(string str)
{
cout << "Value : " << str << endl;
}
void show(char c)
{
cout << "Value : " << c << endl;
}
#endif
2.2 模版概述
模版是 C++ 中特殊的语法规则,主要目的是拓宽数据类型支持的多样性,同时满足数据类型一致化要求,可以用于【函数,类,结构体】。
涉及到的关键字
template<typename 模版数据类型占位符标记>
- 模版数据类型占位符标记,常用的字符有 T(Type), E(Element), K(Key), V(Value), R(ReturnType)
- 占位符标记不具备任何含义,当前程序需要明确数据类型,所有使用到占位符位置的内容都会被替换为【具体类型】
2.3 模版在函数中使用
函数自行声明模版类型,自行使用
template<typename 模版数据类型占位符标记>
返回值类型 函数名(形式参数列表);
在函数中返回值类型和形式参数列表,都可以使用模版类型。
【要求】函数形式参数列表中,必须有一个参数对应当前模版类型,用于【明确约束】当前模版对应的具体数据类型是哪一个类型。
#include <iostream>
using namespace std;
/*
声明当前模版数据类型占位符为 T
可以认为当前函数的声明是带有对应的模版声明内容,后续实现,需要
带有对应的模版声明
*/
template <typename T>
T get_type(T t);
/*
当前函数有两个模版占位符,分别对应 T1 和 T2
两个模版占位符 T1 和 T2 独立,没有任何关系,每一个模版都需要
自行提供对应具体数据类型。
*/
template <typename T1, typename T2>
void test(T1 t1, T2 t2);
/*
当前函数声明模版占位符,同时在形式参数列表中,多个参数使用的模版
都是同一个模版 T
*/
template <typename T>
void test(T t1, T t2, T t3, T t4);
int main(int argc, char const *argv[])
{
/*
原本当前函数的声明
template <typename T>
T get_type(T t);
利用实际参数 10 明确约束当前模版对应的具体数据类型为 int 类型
转换之后
int get_type<int>(int t)
*/
int v1 = get_type(10);
cout << "v1 : " << v1 << endl;
/*
当前提供的实际参数为字符串类型,模版 T 转换为 const char *
const char *get_type<const char *>(const char *t)
*/
const char *str = get_type("换季谨防感冒!!!");
cout << "str : " << str << endl;
cout << "---------------------------------------------" << endl;
/*
当前函数声明了两个模版占位符,每一个模版都需要单独的提供具体数据类型
进行数据约束行为。相互独立,没有任何关系。
void test<int, const char *>(int t1, const char *t2)
*/
test(10, "字符串");
/*
void test<double, float>(double t1, float t2)
*/
test(1.5, 2.0F);
/*
ERROR 语法错误
因为 void test(T t1, T t2, T t3, T t4); 函数中
使用的模版占位符都是 T,第一个实际参数 10 提供给
函数之后,所有使用的模版占位符 T 的位置都是 int 类型。
【模版满足数据类型多样性,同时严格遵守数据类型一致化原则】
后续提供的实际参数 5.5 和 6.0 都是 double 类型,不满足
当前所需参数为 int 类型的要求。【语法错误】
*/
test(10, 5, 5.5, 6.0); // ERROR 错误案例
test(10, 5, 2, 1);
return 0;
}
template <typename T>
T get_type(T t)
{
return t;
}
template <typename T1, typename T2>
void test(T1 t1, T2 t2)
{
cout << "t1 : " << t1 << endl;
cout << "t2 : " << t2 << endl;
}
template <typename T>
void test(T t1, T t2, T t3, T t4)
{
cout << "t1 : " << t1 << endl;
cout << "t2 : " << t2 << endl;
cout << "t3 : " << t3 << endl;
cout << "t4 : " << t4 << endl;
}
2.4 模版在类声明使用
类声明模版类型,可以用于当前类中的成员变量,成员函数,并且支持成员函数二次再定义模版类型。
2.4.1 基本案例
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
/*
模版占位符在明确数据类型的情况下,所有使用对应模版的
类型都会被替换为具体数据类型。
template <typename K, typename V>
class Data
按照语法要求,当前 Data 类声明使用了两个模版占位符,第一个为 K,第二个为 V
编译器认为当前 Data 类完整类型是
Data<K, V>
外部类型要求是 Data 类型
内部类型要求是 <K, V>
[双重数据类型约束]
*/
template <typename K, typename V>
class Data
{
public:
Data();
Data(K key, V value);
Data(const Data &data);
~Data();
void set_key(K key);
K get_key();
void set_value(V value);
V get_value();
private:
K key;
V value;
};
/*
函数在当前类外部进行实现操作
1. 需要明确告知当前使用的模版情况
2. Data 类型在编译器中的真实类型情况为
Data<K, V>;
*/
template <typename K, typename V>
Data<K, V>::Data()
{
/*
this 在构造函数中表示当前实例化对象地址
*/
memset(this, 0, sizeof(Data));
}
template <typename K, typename V>
Data<K, V>::Data(K key, V value)
: key(key),
value(value)
{
}
template <typename K, typename V>
Data<K, V>::Data(const Data &data)
: key(data.key),
value(data.value)
{
}
template <typename K, typename V>
Data<K, V>::~Data()
{
}
template <typename K, typename V>
void Data<K, V>::set_key(K key)
{
// this 在成员函数,表示调用当前函数的对象地址。
this->key = key;
}
template <typename K, typename V>
K Data<K, V>::get_key()
{
return key;
}
template <typename K, typename V>
void Data<K, V>::set_value(V value)
{
this->value = value;
}
template <typename K, typename V>
V Data<K, V>::get_value()
{
return value;
}
int main(int argc, char const *argv[])
{
/*
实例化对象操作,当前 Data 类型的完整数据类型为
Data<K, V>
C++ 要求如果需要实例化带有模版的数据类型,必须在实例化
过程中,对当前模版类型进行明确的数据类型约束。
分为两种情况
1. 实例化对象
2. 利用 new 关键字 + 构造函数形式,申请内存【堆区】空间
进行实例化对象操作
*/
/*
【实例化对象】
利用 Data<K, V> 类内无参数构造函数,<string, int> 是明确
告知当前模版
【K 对应 string 类型, V 对应 int 类型】。
可以认为对应当前 d1 对象,类内的成员内容如下
{
public:
Data(); 【使用无参数构造函数】
~Data();
void set_key(string key);
string get_key();
void set_value(int value);
int get_value();
private:
string key;
int value;
};
*/
Data<string, int> d1;
d1.set_key("方城炝锅烩面");
d1.set_value(12);
cout << "Key : " << d1.get_key()
<< ", Value : " << d1.get_value()
<< endl;
cout << "-----------------------------------------------" << endl;
/*
利用 new 关键字 + 构造函数情况,指针类型必须明确约束模版,同时
在 new 之后的构造函数名称之后需要利用 <> 内容明确约束模版
Data<int, string> * p_data = new Data<int, string>();
明确告知编译器,当前实例化对象 Data 对应的
【K 对应 int 类型, V 对应 string 类型】。
可以认为对应当前 p_data 存储对象地址的指针,类内的成员内容如下
{
public:
Data<int, string>(int k, string value);【使用有参数构造函数】
~Data();
void set_key(int key);
int get_key();
void set_value(string value);
string get_value();
private:
int key;
string value;
};
*/
Data<int, string> *p_data = new Data<int, string>(10, "罗纳尔多");
cout << "Key : " << d1.get_key()
<< ", Value : " << d1.get_value()
<< endl;
delete p_data;
return 0;
}
2.4.2 带有模版的类作为函数参数限制【重点】
data.hpp
#ifndef _MY_DATA_
#define _MY_DATA_
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
template <typename K, typename V>
class Data
{
public:
Data();
Data(K key, V value);
Data(const Data &data);
~Data();
void set_key(K key);
K get_key();
void set_value(V value);
V get_value();
private:
K key;
V value;
};
/*
函数在当前类外部进行实现操作
1. 需要明确告知当前使用的模版情况
2. Data 类型在编译器中的真实类型情况为
Data<K, V>;
*/
template <typename K, typename V>
Data<K, V>::Data()
{
/*
this 在构造函数中表示当前实例化对象地址
*/
memset(this, 0, sizeof(Data));
}
template <typename K, typename V>
Data<K, V>::Data(K key, V value)
: key(key),
value(value)
{
}
template <typename K, typename V>
Data<K, V>::Data(const Data &data)
: key(data.key),
value(data.value)
{
}
template <typename K, typename V>
Data<K, V>::~Data()
{
}
template <typename K, typename V>
void Data<K, V>::set_key(K key)
{
// this 在成员函数,表示调用当前函数的对象地址。
this->key = key;
}
template <typename K, typename V>
K Data<K, V>::get_key()
{
return key;
}
template <typename K, typename V>
void Data<K, V>::set_value(V value)
{
this->value = value;
}
template <typename K, typename V>
V Data<K, V>::get_value()
{
return value;
}
#endif
模版类作为函数参数案例
#include <iostream>
#include "data.hpp"
using namespace std;
/**
* 当前函数所需参数为 Data 类型,但是模版对应的具体数据类型
* 尚未明确,要求当前函数必须对使用的模版进行声明告知。
*
* 【分析】
* 当前函数所需参数为 Data 类型,分为两个部分来分析数据类型
* 限制
* 1. 外部类型为 Data 类型
* 2. 内部类型为 <K, V> 当前是模版情况下,尚未明确
* 当前函数所需参数为 Data 类型,内部类型不限制,可以支持任意
* 内部模版限制类型 Data 对象。
*/
template <typename K, typename V>
void show_data(Data<K, V> &data);
/**
* 当前展示要求的参数必须是 Data 类型,同时限制 Data 中所需的两个
* 模版类型都是 string
*/
void show_data_string_string(Data<string, string> &data);
int main(int argc, char const *argv[])
{
Data<string, int> d1("鱼香肉丝", 13);
Data<int, string> d2(1, "Rose");
Data<string, string> d3("小鸡炖蘑菇", "东北大拉皮");
Data<string, string> d4("黄瓜油条", "炒蒸菜");
cout << "-------------------------------------------" << endl;
/*
void show_data<string, int>(Data<string, int> &data)
*/
show_data(d1);
/*
void show_data<int, string>(Data<int, string> &data)
*/
show_data(d2);
/*
void show_data<string, string>(Data<string, string> &data)
*/
show_data(d3);
show_data(d4);
cout << "-------------------------------------------" << endl;
/*
d1 对应的数据类型为 Data<string, int>
d2 对应的数据类型为 Data<int, string>
外部类型满足当前函数所需,但是内部模版对应的具体数据类型不满足
当前函数所需要的 <string, string>;
d1 和 d2 无法作为函数实际参数,严格遵守数据类型一致化原则。
*/
// show_data_string_string(d1); // ERROR
// show_data_string_string(d2); // ERROR
show_data_string_string(d3);
show_data_string_string(d4);
return 0;
}
template <typename K, typename V>
void show_data(Data<K, V> &data)
{
cout << "Key : " << data.get_key()
<< ", Value : " << data.get_value()
<< endl;
}
void show_data_string_string(Data<string, string> &data)
{
cout << "Key : " << data.get_key()
<< ", Value : " << data.get_value()
<< endl;
}
2.4.3 模版通过继承手段的延续性
#include <iostream>
using namespace std;
/*
Type 情况分析
1. 类声明模版类型 T
2. 类内使用 virtual 修饰声明两个纯虚函数
【带有模版的抽象类】
*/
template <typename T>
class Type // Type<T>
{
public:
virtual void test(T t) = 0;
virtual T get_value(T t) = 0;
};
/*
子类继承带有模版约束的父类,有两种形式
1. 自由模式
2. 妻管严模式
*/
/*
自由模式
子类继承父类,模版对应具体数据类型不明确,使用相同的
模版占位符,在实例化对象时,明确模版对应数据类型。
*/
template <typename T>
class Sub_Type_Freedom : public Type<T>
{
public:
void test(T t)
{
cout << "Value : " << t << endl;
}
T get_value(T t)
{
return t;
}
};
/*
妻管严模式:
子类继承带有模版的父类,继承过程中,父类对应的模版具体数据类型
直接进行明确约束告知。子类没有任何的修改权限
public Type<string>
Type 类继承给子类的函数,对应的模版类型明确为 string 类型
当前 Type 类型继承到子类要求实现的函数为
virtual void test(string t) = 0;
virtual string get_value(string t) = 0;
*/
class Sub_Type_Wife_Always_Right : public Type<string>
{
public:
void test(string t)
{
cout << "t : " << t << endl;
}
string get_value(string t)
{
return t;
}
};
int main(int argc, char const *argv[])
{
/*
自由模式实例化对象明确约束模版对应具体数据类型
*/
Sub_Type_Freedom<string> stf1;
/*
string get_value(string t)
*/
string str1 = stf1.get_value("孤勇者");
cout << "str1 : " << str1 << endl;
/*
inline void test(string t)
*/
stf1.test("夜曲");
cout << "------------------------------" << endl;
Sub_Type_Freedom<int> stf2;
/*
inline get_value(int t)
*/
int ret = stf2.get_value(100);
cout << "ret : " << ret << endl;
stf2.test(1000);
cout << "------------------------------" << endl;
Sub_Type_Wife_Always_Right right;
string str2 = right.get_value("老婆大人说的对!");
cout << "str2 : " << str2 << endl;
right.test("吃砂锅~~~");
return 0;
}
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载必须注明来自 卡卡罗特
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果