c++踩坑大法好 typedef和模板
1,typedef字面意思,自定义一种数据类型
语法:typedef 类型名称 类型标识符;
1),基本用法:
(1) 为基本数据类型定义新的类型名。
(2) 为自定义数据类型(结构体、公用体和枚举类型)定义简洁的类型名称。
(3) 为数组定义简洁的类型名称。
(4) 为指针定义简洁的名称。
简单使用实例:
int main() {
using namespace std;
typedef int hehe;
//相当于定义一个新的数据类型类型
hehe a = ;
hehe(bb) = ;
//这两种实现方法是一样的效果,语法而已
printf("%d\n,%d",a,bb);
typedef int f();
//相当于定义一个返回int的函数f
f(daqing);
//等价于声明int daqing(),相当于daqing这个变量也是一个函数名称,实现在main后面
int test = daqing();
printf("test%d",test);
return ;
}
int daqing() {
return ;
}
2),typedef结构体语法,并且取别名
using namespace std;
typedef struct node {
int data;
char test;
}tree;
//声明一个结构体struct node的别名为tree
int main() {
tree atree;
atree.data = ;
//根据结构体的别名初始化变量
node hehe;
hehe.data = ;
//根据结构的本名初始化变量
return ;
}
3),typedef声明指针类型
实例1,
using namespace std; typedef int hehe, hehe2;
//此处可以理解为声明了两种数据类型,一个是hehe类,另一个是hehe2类,两者实际上都是int
typedef int hehe3, *hehe4;
//此处声明了两种数据类型,一种是hehe3类,实际上就是int,另一个是hehe4这个指针数据类型,如果把*hehe4看成一个整体,
//那么这个整体储存的是指向实体的指针,而hehe4是存储的是实体的地址
int main() {
hehe a = ;
hehe2 b = ;
//分别创建了a和b作为结构的实体 hehe3 he3 = ;
//创建了he3为hehe3的结构实体
int aa = ;
hehe4 bb = &aa;
//创建bb为hehe4这个指针数据类型的结构实体,因为hehe4是一个指针数据类型,所以bb必然也是一个指针,必须按照指针的规则赋值 return ;
}
实例二,看我typedef一个结构体指针
using namespace std;
typedef struct {
int data;
LNode *next;
}LNode, *LinkList;
//一个上述的struct声明了一种特殊类型LinkList,这个类型表示变量是一个指针 int main() {
LNode hehe;
LinkList daqing = &hehe;
//LinkList数据类型是一个指针数据类型,所以,意思是,声明一个指向LinkList数据类型的指针,然后把已经初始化的LNode类型的变量hehe的地址赋值给这个地址
return ;
}
2,模板(函数模板)
1),模板简单理解:
首先模板是针对编译器使用的,它就是告诉编译器如何定义函数,比如如下的例子:
template <typename T>
//声明一个模板,第一个参数的固定的,模板名叫T
void Swap(T &a,T &b){
//省略
}
当int变量需要使用Swap的时候,T就变成了int,如果是double变量要使用该函数,T就变成了double,所以说,对计算机来说,计算量丝毫没少。
模板允许只定义一次函数的实现,即可使用不同类型的参数来调用该函数。这样做可以减小代码的书写的复杂度,同时也便于修改。
c++中模板存在的意义:
如果是python,想要交换两个变量的内容:
def exchange(x,y):
a=x;
x=y;
y=a;
return (x,y)
#整数交换
x,y=1,10
x,y=exchange(x,y)
print(x,y)
#字符串交换
x,y="a","bcd"
x,y=exchange(x,y)
print(x,y)
但是如果是c++,这样做明显是不行的,本人菜鸟,写出交换两个整数的代码如下:
void daqing(int *x,int *y);
void daqing(int *x,int *y) {
int a = *x;
*x = *y;
*y = a;
}
int main() {
using namespace std;
int a = ;
int b = ;
daqing(&a,&b);
//此处相当于把a,b的地址传递给了daqing函数,而daqing函数拿到的是*&a,*&b,(x和y相当于&a,&b)相当于a和b的值,刚开始*x=1,*y=2,int a 作为局部变量保存了*x的值,1,然后x,y交换。
printf("%d,%d\n",a,b);
return ;
}
如果想交换两个char或者double,那就得把代码copy一遍,然后把声明和实现的代码中的类型全都变了,好费劲啊,所以这时候我们就需要模板啦。
书上的实例:
#include "pch.h"
using namespace std;
template <typename AnyType>
//电脑电脑,我要建立一个模板,模板名称是AnyType,关键字template和typename是必须的
void Swap(AnyType &a, AnyType&b); int main() {
int a = ;
int b = ;
Swap(a,b);
printf("%d,%d\n",a,b);
double aa = ;
double bb = ;
Swap(aa,bb);
printf("%f,%f\n", aa, bb);
string aaa = "";
string bbb = "abc";
Swap(aaa,bbb);
cout << aaa << " " << bbb << endl;
return ;
} template <typename AnyType>
void Swap(AnyType &a, AnyType&b) {
AnyType temp;
temp = a;
a = b;
b = temp;
}
//模板我来理解大约是这么个意思,就是告诉电脑,我要新建一个临时类型,类型名是自己定义的,比如anytype,等到需要用的时候,如果用的是int,那就用int代替anytype,如果是char,就用char代替anytype
注意,函数模板不能缩短可执行程序,我的理解是,swap函数确实生成了int版本的函数,double版本和string版本,并非只有一个函数兼容了不同类型,所以对电脑来说计算量丝毫没有少哦。而模板的好处是,生成多个函数的定义更加可靠,简单。
2),模板不影响重载
//以下生命方法是没问题的,实现省略了,调用swap函数的时候,传入的参数符合哪一个重载函数,就使用哪个
template<typename T>
void Swap(T &a,T &b); template<typename T>
void Swap(T a[], T b[], int n);
3),显式具体化
个人理解:一个函数模板,可以生成int,double,string等多种不同的具体函数,可以针对某一种特殊的类型进行特殊的操作,比如swap这个模板函数,一般情况下实现a和b的互换,它对job结构进行了显式具体化以后,就可以实现a的某个属性和b的某个属性互换了。嗯嗯
#include <iostream>
using namespace std;
struct job {
char name[];
double salary;
int floor;
}; template<typename T>
void Swap(T &a,T &b);
//以上是普通声明 template<> void Swap<job>(job &j1, job &j2);
//这是一个显式具体化的声明,意思是:
//不要使用swap模板类生成函数定义,应该使用专门为int类型显示地定义int模板来实现这个函数
void show(job& j); int main() {
int i, j;
i = ;
j = ;
Swap(i,j);
//此处使用隐式实例化,使用模板生成函数定义,模板通过传入的参数i和j判断需要用int,生成了swap的一个int实例
job sue, sidney;
sue = { "sue",73.23, };
sidney = { "dsidney",55.23, };
Swap(sue, sidney);
//如果swap没有那个重载的显示具体化声明,调用swap以后,sue和sidney会互换,但是sue的工资仍旧是73.23,仍在一楼,sidney也没变,但是
//既然已经有了针对job这个结构专门定义的job模板,所以系统会调用那个显示具体化的swap函数,sue的工资会变成55.23,楼层会变成2楼。
show(sue);
//name is s salary 55.230000 floor 2
return ;
} template<typename T>
void Swap(T &a, T &b) {
T temp;
temp = a;
a = b;
b = temp;
}
template<> void Swap<job>(job &j1, job &j2) {
double temp1;
int temp2;
temp1 = j1.salary;
temp2 = j1.floor;
j1.salary = j2.salary;
j1.floor = j2.floor;
j2.salary = temp1;
j2.floor = temp2;
}
void show(job &j){
printf("name is %c salary %lf floor %d", j.name[], j.salary, j.floor);
//不想用cout,所以打印出来s是sue,d是sidney
}
4),显式实例化
使用某个函数模板的时候,显式地说告诉电脑,我需要一个某类型的模板函数(写为这样:add<double>(aa, bb)。),而不是让电脑根据传入的参数自己判断。
#include <iostream>
using namespace std; template<typename T>
T add(T a,T b);
//以上是普通声明 int main() {
double result;
int aa = ;
double bb = ;
//result = add(aa, bb);
//普通调用add会报错,因为参数aa说T是int,bb说T是个double,然后系统就懵了,
result = add<double>(aa, bb);
//显式地声明add需要使用double生成模板,然后把aa强制转换成double,所以这样跑起来是没问题的
//这就是显式实例化
printf("%lf\n", result); return ;
}
template<typename T>
T add(T a, T b) {
return a + b;
}
3,类模板
1),模板基础
普通思想实现一个栈是这样的(本应该头文件和源文件分开,考虑到展示问题,干脆合起来了,请自行分开)
#include "pch.h"
using namespace std;
typedef unsigned long Item;
//定义一个类型,类型名是Item(实际上就是无符号整形),这样写的好处在于,unsigned long如果想变成int,可以直接改动一处。
class Stack
{
private:
enum {MAX=};
//枚举,此处相当于定义了一个整形MAX变量,
Item items[MAX];
//建立一个数组,数组长度为10,数组以Item类型填充
int top;
public:
Stack();
//构造函数
const bool isEmpty();
const bool isfull();
bool push(const Item &item);
bool pop(Item &item);
//注意,以上都是引用传参,在函数内部修改参数值,不必return外部的参数也会变化
};
int main() {
Stack zhan;
Item a = ;
Item b = ;
Item c ;
zhan.push(a);
zhan.push(b);
//添加两个元素到栈里
zhan.pop(c);
//拿出栈顶的元素,元素值用变量c来存储
cout << c << endl;
return ;
}
Stack::Stack() {
top = ;
}
const bool Stack::isEmpty() {
return top == ;
}
const bool Stack::isfull() {
return top == MAX;
}
bool Stack::push(const Item &item) { if (top < MAX) {
items[top++] = item;
//注意,此处的命令相当于top=top+1;items[top]=item;
cout <<"push command,amount is:"<<top << endl;
return true;
}
else
return false;
}
bool Stack::pop(Item &item) { if (top>) {
item = items[--top];
//注意,此处的命令相当于:item=items[top];top=top-1;千万小心别理解错了
cout << "pop command,amount is:" <<top<< endl;
return true;
}
else
return false;
}
使用模板类实现的栈是这样的:
#include "pch.h"
using namespace std;
template <class Type>
//定义一个叫Type的类模板和stack类紧紧关联在一起,甚至分号都不用写了,囧。。。
class Stack
{
private:
enum {MAX=};
//枚举,此处相当于定义了一个整形MAX变量,
Type items[MAX];
//建立一个数组,数组长度为10,数组以Item类型填充
int top;
public:
Stack();
//构造函数
bool isEmpty();
bool isfull();
bool push(const Type &item);
bool pop(Type &item);
//注意,以上都是引用传参,传递的参数是模板,就是在函数内部修改参数值,不必return外部的参数也会变化
};
int main() {
Stack<int> zhan;
//注意,使用栈实例的时候就不能再写Type这样的模板代号了,要写真正想要实例化的数据类型
int a = ;
int b = ;
int c ;
zhan.push(a);
zhan.push(b);
//添加两个元素到栈里
zhan.pop(c);
//拿出栈顶的元素,元素值用变量c来存储
cout << c << endl; Stack<string> strzhan;
//实例化一个string为模板类型的实例
string aa = "abc";
string bb = "aa0";
string cc;
strzhan.push(aa);
strzhan.push(bb);
//添加两个元素到栈里
strzhan.pop(cc);
//拿出栈顶的元素,元素值用变量c来存储
cout << cc << endl; typedef double idouble;
Stack<idouble> dbzhan;
//实例化一个自己定义的类型为模板类型的实例,这样竟然也可以,厉害厉害
idouble aaa = ;
idouble bbb = ;
idouble ccc;
dbzhan.push(aaa);
dbzhan.push(bbb);
//添加两个元素到栈里
dbzhan.pop(ccc);
//拿出栈顶的元素,元素值用变量c来存储
cout << ccc << endl;
return ;
}
template <class Type>
Stack<Type>::Stack() {
top = ;
}
//注意,实现的时候,每个函数都需要加上模板信息,否则报错,语法问题,记住就是了
//注意,普通函数实现的写法是这样的:Stack::Stack(){},但是使用了模板的函数实现的写法是这样的:Stack<Type>::Stack(){},尖括号用于说明,我是一个模板类
template <class Type>
bool Stack<Type>::isEmpty() {
return top == ;
}
template <class Type>
bool Stack<Type>::isfull() {
return top == MAX;
}
template <class Type>
bool Stack<Type>::push(const Type &item) {
if (top < MAX) {
items[top++] = item;
//注意,此处的命令相当于top=top+1;items[top]=item;
cout <<"push command,amount is:"<<top << endl;
return true;
}
else
return false;
}
template <class Type>
bool Stack<Type>::pop(Type &item) {
if (top>) {
item = items[--top];
//注意,此处的命令相当于:item=items[top];top=top-1;千万小心别理解错了
cout << "pop command,amount is:" <<top<< endl;
return true;
}
else
return false;
}
2),多个参数的模板
因为模板是编译器对某些特殊字符的替换,所以模板内带的参数也可以是非常具体的数值,比如说,整数5,废话不多说,看例子
#include <stdarg.h>
#include <iostream>
#include <string>
#include <memory> //shared_ptr
#include <vector>
using namespace std; template<class T, int n>
class Father {
private:
T ar[n];
public:
Father() {};
explicit Father(const T &v);
virtual T &operator[](int i);
//virtual T operator[](int i) const;
};
template<class T, int n>
Father<T, n>::Father(const T &v) {
for (int i = ; i < n; i++) {
ar[i] = v;
}
} template<class T, int n>
T &Father<T, n>::operator[](int i) {
if (i< || i>n) {
printf("out range");
exit(-);
}
return ar[i];
};
//template<class T, int n>
//T Father<T, n>::operator[](int i) const {
// if (i<0 || i>n) {
// printf("out range 2nd");
// exit(-1);
// }
// return ar[i];
//}
int main(void)
{
Father<double,> f1(1.0);
//编译器定义了名为Father<double,5>的一个类,并且创建了该类的对象叫f1,传入了参数1.0
//该实例内部创建了一个长度为5内容都是1.0的double数组
Father<double,> f2(2.0);
//编译器定义了名为Father<double,6>的一个类,并且创建了该类的对象叫f2,传入了参数2.0 double hehe=f1.operator[]();
//把f1保存的数组中的数组的第二个元素拿出来看一下,,果然是1.0
//这代码有点傻啊,写个例子还得定义一个重载函数,好吧被我注释掉了,我是书上抄的。
printf("%lf", hehe); return ;
}
不过例子归例子,以上这样的写法并不通用,因为模板参数每变一次就生成了新的class,不如 classname<int> instance(12)这样的写法通用
c++踩坑大法好 typedef和模板的更多相关文章
- thinkphp5踩坑之部署到服务器模板不存在
一个项目部署到Linux服务器上去的时候,发现某些模板竟然会报错说"模板不存在:/Application/Admin/-.", 解决方法:网上有说是因为使用$this->fe ...
- c++ 踩坑大法好 复合数据类型------vector
1,vector是啥? 是具有动态大小的数组,具有顺序.能够存放各种类型的对象.相比于固定长度的数组,运行效率稍微低一些,不过很方便. 2,咋用? 声明: vector <int> vi; ...
- c++ 踩坑大法好 枚举
1,枚举是个啥? c++允许程序员创建自己的数据类型,枚举数据类型是程序员自定义的一种数据类型,其值是一组命名整数常量. ,wed,thu,fri,sat,sun}; //定义一个叫day的数据类型, ...
- c++踩坑大法好 数组
1,c++遍历数组 int数组和char数组不同哦,int占4位,char占1未,同理double也不同.基本遍历方法: ] = { ,,, }; ]); printf("len of my ...
- c++踩坑大法好 赋值和指针的区别
1,先说结论: 两个指针指向同一个结构,一个改了结构,另一个也会改掉. 两个指针指向同一个结构,修改了其中一个的指向,并且改了其中的内容,另一个不为所动. 2,看例子 main.cpp #includ ...
- c++ 踩坑大法好 char字符,char数组,char*
1,基本语法 1,定义一个char字符: char hehe='a'; //单引号 2,定义一个由char字符组成的数组: char daqing[] = "abcd"; char ...
- c++踩坑大法好 宏定义 头文件
1,c++宏定义是干啥的?防止重复引用,如何防止重复引用? //a.h //声明一个类,和其他声明 #include <iostream> class A{ public: static ...
- 我的微信小程序入门踩坑之旅
前言 更好的阅读体验请:我的微信小程序入门踩坑之旅 小程序出来也有一段日子了,刚出来时也留意了一下.不过赶上生病,加上公司里也有别的事,主要是自己犯懒,就一直没做.这星期一,赶紧趁着这股热乎劲,也不是 ...
- vue+ vue-router + webpack 踩坑之旅
说是踩坑之旅 其实是最近在思考一些问题 然后想实现方案的时候,就慢慢的查到这些方案 老司机可以忽略下面的内容了 1)起因 考虑到数据分离的问题 因为server是express搭的 自然少 ...
随机推荐
- Windows10访问Ubuntu子系统(WSL)的桌面环境
原文地址:https://blog.csdn.net/xmh19936688/article/details/90212960 Windows10访问Ubuntu子系统(WSL)的桌面环境文章目录Wi ...
- C#方法中的各类参数
居家隔离的第26天,还在持续的疫情着实让人担忧,看着每天新增的确认人数数字,也在为那些家庭祝福,每当想想那不是一个数字是一条条鲜活的生命时就格外沉重.利用闲在家里的时间巩固C#语言的一个难点.最近在温 ...
- Phpstorm 2020-01-04试了可用的激活码【亲测可用】WebStrom
[直接点击试用30天] http://myphp.vip/ 测试时间:2018-10-12可用(v2019.2) 测试时间:2019-12-24可用(v2019.2) 测试时间:2020-01-04可 ...
- Vim 全选命令
ggVG稍微解释一下上面的命令gg 让光标移到首行,在vim才有效,vi中无效V 是进入Visual(可视)模式G 光标移到最后一行选中内容以后就可以其他的操作了,比如:d 删除选中内容y ...
- 数字孪生 VS 平行系统
数字孪生和平行系统作为新兴技术,在解决当今人工智能邻域面临的信息量大,干扰信息不确定因素多,与人的参与沟通更加紧密,人机互动更加重视,为了使人们有更好的体验人工智能带来的便利,急需推动信息物理社会的高 ...
- Easyui-Treegrid使用注意事项-sunziren
版权声明:本文为sunziren原创文章,博客园首发,转载务必注明出处以及作者名称. 最近,工作中有一个网页需要用到前端框架easyui的treegrid组件,因此我对这个treegird研究了一段时 ...
- SQL语句中设置字段值取反操作
1.对布尔值取反,使用 ~. 如 update set status=~status where id=2; status的值为true || false. 2.对0.1 数值取反,使用abs() 取 ...
- Java基于SSM在线学习系统设计与实现
Spring+SpringMVC+MyBatis+Bootstrap+Vue开发在线学习系统 本课题的主要内容是开发基于Java EE的在线学习平台,使用MVC经典开发模式. ...
- Linux c++ string转其他类型
#include <iostream> #include <sstream> #include <string> using namespace std; temp ...
- Android 判断APP前台,后台运行
public void checkAppState() { ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVI ...