Lever_3_C++进阶-STL_Day16-20

Day_16

1-函数模版

​ C++可以通过 重载 写很多一样名字的函数,然后根据参数进行选择调用:

void swap1(char &a, char &b)
void swap1(int &a, int &b)

​ 但是这样还不够简单!可以通过 函数模板 的方式,就只用写一个了!(和写函数差不多)。

函数模板的本质是类型的参数化。

template <typename T1>	//写多少种T就要用多少种
void myswap(T1 &a, T&b)
{
    T1 c = 0;
    c = a;
    a = b;
    b = c;
}

template 关键字就是告诉编译器:我要进行泛型编程了。

1.1函数模板定义形式 :
template    < 类型形式参数表 >

//类型形式参数的形式为:(写了多少个就要用多少个)
typename T1 ,  typename T2 , …… , typename Tn   
或	class T1 ,  class T2 , …… , class Tn  	   

    //用  typename  或  class都可以

【注】在模板定义语法中关键字 class 与t ypename 的作用完全一样。

​ 不过,ypename另外一个作用为:使用 **嵌套依赖类型 **。

1.2函数模板调用
//两种都可以
myswap<float>(a, b);        //推荐!显示类型调用

myswap(a, b);               //自动数据类型推导 

2-函数模板 遇上 函数重载

  • 函数模板不允许自动类型转化
  • 普通函数能够进行自动类型转换

函数模板可以像普通函数一样被重载
C++编译器优先考虑普通函数
如果函数模板可以产生一个更好的匹配,那么选择模板
可以通过空模板实参列表的语法限定编译器只通过模板匹配

template <typename T>
void myswap(T &a, T &b)
{
	T c = 0;
	c = a;
	a = b;
	b = c;
	cout << "hello ....我是模板函数 欢迎 calll 我" << endl;
}

void myswap(int a, char c)
{
	cout << "a:" << a << "c:" << c << endl;
	cout << "我是普通函数 欢迎来访" << endl;
}

当调用函数:

int main()
{
	int		a = 10;
	char	c = 'z';
	myswap(a, c); // 普通函数的调用:  可以进行隐式的类型转换 
	myswap(c, a); //进行隐式的类型转换 ASCII码
	myswap(a, a); // 函数模板函数的调用(本质:类型参数化): 将严格的按照类型进行匹配,不会进行自动类型转换
}

但是我要是写一个一样的函数一样的类型:

void myswap(int a, int c)
{
    cout << "a:" << a << "c:" << c << endl;
    cout << "我是普通函数 欢迎来访" << endl;
}

那么在调用时:

myswap<int>(a, c);   // 函数模板的调用
myswap(c, a); 		 //普通函数的调用
myswap<>(a, c);   	 // 这样写也会优先选择 函数模板
myswap<int>(a, a);	 // 函数模板的调用

所以在用函数模板的时候最好还是 显示调用

3-函数模板机制探究

在这里插入图片描述

3.1-函数模板机制结论

两次编译原理:

编译器 并不是把 函数模板 处理成能够处理任意类的函数;

编译器 从 函数模板 通过具体类型产生不同的函数;

编译器会对函数模板进行 两次编译;

在声明的地方对 模板代码 本身进行 编译;在调用的地方对 参数替换后的代码 进行 编译

4-类模板

4.1-为什么需要类模板?

​ 类模板 与 函数模板 的定义和使用类似。

​ 有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同。

  • 类模板用于实现类所需数据的 类型参数化 ;

  • 类模板在表示如 数组、表、图 等数据结构显得特别重要,

    这些数据结构的表示和算法不受所包含的元素类型的影响。

4.2-单个 类模板 语法

以往类的写法:

class A{
public:
    A(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    void Print()
    {
        cout << "a: " << a << "  b: " << b << endl;
    }
private:
    int a;
    int b;
};

用模板后:(这里我使用了a b 两个不同的类型)

template <class T1, class T2>
class A{
public:
    A(T1 a, T2 b)
    {
        this->a = a;
        this->b = b;
    }
    void Print()
    {
        cout << "a: " << a << "  b: " << b << endl;
    }
private:
    T1 a;
    T2 b;
};

int main()
{
    A<int,char> aa(1,'c');			//调用的时候记得写上类型
}
4.3-类模板 做 函数参数
void UseA( A<int> &a )			//要写参数类型
{
	a.printA();
}

main()
{
	//模板类(本身就是类型化的)====具体的类=====>定义具体的变量
	A<int> a1(11), a2(20), a3(30); //模板类是抽象的  ====>需要进行 类型具体

	UseA(a1);
	UseA(a2);
	UseA(a3);
}
4.4-从模板类 派生出 类 的写法
template <class T1>
class A{
public:
    A(T1 a, T1 b)
    {
        this->a = a;
        this->b = b;
    }
    void Print1()
    {
        cout << "a: " << a << "  b: " << b << endl;
    }
public:
    T1 a;
    T1 b;
};

class B : public A<int>		//记得写类型
{
public:							//这里要对 A的类型说明
    B(int c=0, int d=0, int a=0, int b=1) : A<int>(a,b)
    {
        this-> c = c;
        this-> d = d;
    }
    void Print2()
    {
        cout << "c: " << c << " d " << d << " a: " << a << endl;
    }
private:
    int c;
    int d;
};


int main()
{
    B b1(1,2,3,4);
    b1.Print2();

}
4.5-从 模板类 派生 模板类
template <class T>
class C : public A<T>
{
public:
	C(T c, T a) : A<T>(a)
	{
		this->c = c;
	}
	void printC()
	{
		cout <<  "c:" << c <<endl;
	}
protected:
	T c;
};

5- 类模板语法知识体系梳理

5.1-所有的类模板函数写在类的内部

​ 写了一个 友元函数调用, 写了一个运算符重载

template <typename T>
class Complex
{
    //写了一个友元函数
	friend Complex MySub(Complex &c1, Complex &c2)
	{
		Complex tmp(c1.a - c2.a, c1.b - c2.b);
		return tmp;
	}

public:
	Complex(T a, T b)	//因为不知道 a b的数据类型,所以不给初始值
	{
		this->a = a;
		this->b = b;
	}

	Complex operator+ (Complex &c2)		//写了一个运算符重载
	{
		Complex tmp(a+c2.a, b+c2.b);
		return tmp;
	}

	void printCom()
	{
		cout << "a:" << a << " b: " << b << endl;
	}
private:
	T	a;
	T	b;
};

void main()
{
	//需要把模板类 进行具体化以后  才能定义对象  C++编译器要分配内存
	Complex<int>	c1(1, 2);
	Complex<int>	c2(3, 4);

	Complex<int> c3 = c1 + c2;
	//c3.printCom();
    
	Complex<int> c4 = MySub(c1, c2);
	cout << c4 << endl;
}
5.2-所有的类模板函数写在类的外部,在一个cpp中

​ 把函数实现 弄成 函数声明,这样就可以把函数写在外面了。

​ 不要乱用 友元函数,容易出错。

在函数实现前要加上 模板声明:

template <typename T1>;

同理, 类的函数 要加上 类的域名,又因为它是模板类,所以还要加上参数列表:

template <typename T>
Complex<T>::Complex(T a, T b)	//构造函数在类的外部实现
{
	this->a = a;
	this->b = b;
}

template <typename T>
void Complex<T>::printCom()
//要是返回Complex的话就这样写:Complex<T> Complex<T>::printCom()   
{
	cout << "a:" << a << " b: " << b << endl;
}

在类的内部声明就行:

template <typename T>
class Complex
{
public:
	Complex(T a, T b);
	void printCom();
	
private:
	T	a;
	T	b;
};
5.3-所有的类模板函数写在类的外部,在不同的.h和.cpp中

​ 可以把类写到一个 .h 文件里,在里面记得声明 成员函数;

									//文件名为 Others.h
#include <iostream>

using namespace std;

template <class T>
class Complex
{
public:
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }

    void printCom();		//声明成员函数

private:
    T	a;
    T	b;
};

​ 成员函数写在另一个 .h 文件里,记得要引用类的说明;

#include <iostream>
#include "Others.h"		//引用类的 .h 头文件

using namespace  std;

template <class T>		//引用类说明
class Complex;

template <typename T>
void Complex<T>::printCom()		//类中的printCom,作用域说明符
{
    cout << "a:" << a << " b: " << b << endl;
}

6-类模板总结

归纳以上的介绍

类模板的用途 就是让我们的 数据 与 算法 分离

可以这样声明和使用类模板:

1- 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。

2- 将此类中准备改变的类型名( 如 int 要改变为 float 或 char )改用一个自己指定的虚拟类型名(如上例中的 T1 )。

3- 在类声明前面加入一行,格式为:

template <class 虚拟类型参数>

如:

template <class numtype> //注意本行末尾无分号

class Compare
{…}; //类体

4- 用类模板定义对象时用以下形式:

类模板名<实际类型名> 对象名;

类模板名<实际类型名> 对象名(实参表列);

如:

Compare<int> cmp;

Compare<int> cmp(3,7);
  1. 如果在类模板外定义成员函数,应写成类模板形式:
 template <class 虚拟类型参数>

函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}

//eg.
template <typename T>
void Complex<T>::printCom()
{
    cout << "a:" << a << " b: " << b << endl;
}
关于类模板的几点说明:

1- 类模板的类型参数可以有一个或多个,每个类型前面都必须加 class ,如:

template <class T1,class T2>

class someclass

{…};

在定义对象时分别代入实际的类型名,如:


                
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这段代码是在使用 PyQt/PySide 的信号与槽机制,它的作用是将 `self.choose_action.getValue` 和 `self.lever_choose_action.getValue` 两个槽与 `self.proportion_Value` 信号连接起来,当 `self.proportion_Value` 发射信号时,两个槽中的函数都会被调用。 如果你想修改这段代码,可以根据你的需求进行如下操作: 1. 只连接一个槽:如果你只想连接一个槽,可以注释掉另外一个槽的连接语句。比如,如果你只想连接 `self.choose_action.getValue` 这个槽,可以这样写: ``` self.proportion_Value.connect(self.choose_action.getValue) # self.proportion_Value.connect(self.lever_choose_action.getValue) ``` 2. 连接多个槽:如果你想连接更多的槽,可以添加类似的代码。比如,如果你还想连接一个名为 `another_slot` 的槽,可以这样写: ``` self.proportion_Value.connect(self.choose_action.getValue) self.proportion_Value.connect(self.lever_choose_action.getValue) self.proportion_Value.connect(self.another_slot) ``` 3. 更改连接方式:如果你想使用不同的连接方式,可以使用 `QtCore.QObject.connect()` 方法来进行连接。比如,如果你想使用 Qt5 的新连接方式,可以这样写: ``` self.proportion_Value.connect(self.choose_action.getValue, QtCore.Qt.ConnectionType.DirectConnection) self.proportion_Value.connect(self.lever_choose_action.getValue, QtCore.Qt.ConnectionType.DirectConnection) ``` 这里我们使用了 `QtCore.Qt.ConnectionType.DirectConnection` 参数来指定连接类型,它表示使用直接连接方式。你也可以使用其他连接方式,比如 `QtCore.Qt.AutoConnection`、`QtCore.Qt.QueuedConnection` 等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值