C++ 常见运算符重载及模板
2016-11-22 22:10
253 查看
1. 运算符重载
概念:当运算符作用于类类型的独享是,可以通过运算符重载重新定义该运算符的含义。
他们的名字由关键字operator和要重载的运算符组成。
重载运算符的参与格式和该运算符作用的运算对象一样多。
注:
(1).如果一个运算符函数的成员函数,则它的第一个运算对象绑定到this上。
(2).对于运算符函数俩说,它或是类的成员,或至少含有一个类类型的参数。
(3).只能重载现有的运算符,而不能新发明运算符
(4).对于重载的运算符来说,其优先级和结合律于对应的内置运算符保持一致
(5).一般情况下不重载&,&&,||和逗号运算符,因为他们的求值顺序无法保存,而且无法保留短路效应
不能重载的运算符:
1. * 指针运算符 2. :: 域运算符 3.sizeof 4. ? : 条件运算符 5. . 成员运算符
1.1.重载算术关系运算符
通常情况下,我们把算是关系运算符定义为非成员函数,以允许对左右侧对象进行转换。
但事实上下面成员函数和普通函数都可以实现:(建议还是上述)
这里举一个分数的加法为例子
(1).成员函数
下面是个重载"+"的例子:
注:返回值:该对象的类型(这里这个返回值不能为引用,引用对象在函数失效后就释放)
(2).普通函数
同样以上面+为例
注:当类操作符和全局操作符同时被重载时,类操作符被优先调用。
1.2.重载输入输出操作符
做输入输出表示时:重载的必须为非成员函数。
这个主要受第一个参数是istream或ostream的限制。
例子在上一个例子中的第二个中体现
通常情况下,输入输出运算符第一个形参时非常量的引用。因为流的输入会使状态改变。返回值是流的引用。
1.3.重载关系操作符
关系操作符:<,>.<=,>=,==,!=
重载关系运算符要和原有的关系运算符的思维相近
成语函数的写法:
非成语函数的写法:
注:返回值是一个bool类型。如果定义了算术运算符,则一般也会定义个对应的复合复制运算符。
参数:可以重载(比如和一个固定的字符串相比)
1.4.重载下标运算符
[ ]用做访问一组元素中的某个元素。
也可称为:索引,唯一标识一个元素
下标运算必须是成语函数
成语函数的写法:
参数:类型可以自己选择,用于指定元素下标
注:为了与普通下标元素兼容,返回值为元素的引用。一般会重载2个版本,一个返回是普通引用,另一个如果类是常量则返回常量引用。
1.5.重载递增递减运算符
递增递减运算符建议设计成成员函数
成语函数的写法:
返回值为元素的引用
可以加参数设计成区分前后置的,只需要在参数列表里加一个区分参数就可以
1.6.重载类型转换操作符()
转换构造函数和类型转换运算符共同定义了类类型转换。
类型转换运算符是类的一个特殊成语函数,他负责将一个类类型转换成其他类型。
一般形式如下:
类型运算符既没有显示的返回类型也没有形参,且必须定义成类的成员函数。
Type 类似于返回转化目标类型。除void外
不允许转换成数组或者函数类型,但允许转换成指针或引用类型。
1.7.函数调用运算符
如果类重载了函数调用运算符,则我们可以向使用函数一样使用该类的对象。
所以函数调用运算符必须是成员运算符。
举个例子:
如果类定义了调用运算符,则该类的对象称作函数对象
2.模板
一个模板就是一个创建类或者函数的蓝图或者说公式
注:函数模板和类模板通常定义在头文件中。模板头文件应该包括模板的定义以及类模板或成员定义中用到的所有名字的声明。
2.1.函数模板
概念:一个函数模板就是一个公式,可以用来生活针对特定类型的函数版本
语法:
template <typename T>
T function(T a, T b)
{
T c;
...
return c;
}
模板参数列表是由一个逗号分隔一个或多个模板参数的列表,并用<>包围起来。
用模板类型可替代后面函数中出现所有的同名的类型。
注意:typename是关键字也可以用class代替,但为了不产生歧义,一般用typename
使用:
function <int>(val, 4);
在调用一个函数模板时,编译器会根据函数参数来自动推断模板参数,然后用推断的模板参数进行实例化成一个特定版本。
同时输入参数可以为常量,编译器也会自动转换。(这里还有个非模板类型参数的概念...感觉和动态识别常量有关先略过...)
函数模板用于实现通用算法,有人称之为泛型算法。
2.2.类模板
概念:类模板用来生成类的蓝图。
语法:
template <typename T>
class temp
{
}
调用方式:
temp<int> tmp;
temp<string> tmp2;
注:
1.一个模板类生成的每个实例都是一个独立的类
2.和函数模板不同的是,编译器不能为类模板推断模板参数类型
关于类模板名的使用:
下面是一个例子:
概念:当运算符作用于类类型的独享是,可以通过运算符重载重新定义该运算符的含义。
他们的名字由关键字operator和要重载的运算符组成。
重载运算符的参与格式和该运算符作用的运算对象一样多。
注:
(1).如果一个运算符函数的成员函数,则它的第一个运算对象绑定到this上。
(2).对于运算符函数俩说,它或是类的成员,或至少含有一个类类型的参数。
(3).只能重载现有的运算符,而不能新发明运算符
(4).对于重载的运算符来说,其优先级和结合律于对应的内置运算符保持一致
(5).一般情况下不重载&,&&,||和逗号运算符,因为他们的求值顺序无法保存,而且无法保留短路效应
不能重载的运算符:
1. * 指针运算符 2. :: 域运算符 3.sizeof 4. ? : 条件运算符 5. . 成员运算符
1.1.重载算术关系运算符
通常情况下,我们把算是关系运算符定义为非成员函数,以允许对左右侧对象进行转换。
但事实上下面成员函数和普通函数都可以实现:(建议还是上述)
这里举一个分数的加法为例子
(1).成员函数
下面是个重载"+"的例子:
int gcd(int a, int b) { return (b ? gcd(b, a%b) : a);//求最大公约数 } class Fraction { public: Fraction(int num = 1, int deno = 1) { numerator = num; denominator = deno;} ~Fraction(){} Fraction operator+(const Fraction &oper); ostream & display(ostream & os); public: int numerator; //分子 int denominator; //分母 }; //重载+运算符 Fraction Fraction::operator+(const Fraction &oper) { Fraction result; result.denominator = denominator * oper.denominator; result.numerator = denominator*oper.numerator + oper.denominator*numerator; int g = gcd(result.denominator, result.numerator); //求公约数 result.denominator /= g; result.numerator /= g; return result; } ostream & Fraction::display(ostream & os) { if (denominator == 1) os << "result: " << numerator; else os << "result: " << numerator << '/' << denominator; return os; } int main() { Fraction *op1 = new Fraction(3, 5); Fraction *op2 = new Fraction(12, 2); Fraction op3 = *op1 + *op2; op3.display(cout) << endl; getchar(); return 1; }
注:返回值:该对象的类型(这里这个返回值不能为引用,引用对象在函数失效后就释放)
(2).普通函数
同样以上面+为例
int gcd(int a, int b) { return b?gcd(b,a%b):a; } class Fraction { public: Fraction(int num = 1, int deno = 1) { numerator = num; denominator = deno;} ~Fraction(){} public: int numerator; //分子 int denominator; //分母 }; //普通函数重载 Fraction operator+(const Fraction &op1, const Fraction &op2) { Fraction result; result.denominator = op1.denominator * op2.denominator; result.numerator = op1.denominator*op2.numerator + op2.denominator*op1.numerator; int g = gcd(result.denominator, result.numerator); //求公约数 result.denominator /= g; result.numerator /= g; return result; } //重载<<运算符 ostream & operator<< (ostream & os, const Fraction& op) { if (op.denominator == 1) os << "result: " << op.numerator; else os << "result: " << op.numerator << '/' << op.denominator; return os; } int main() { Fraction *op1 = new Fraction(3, 5); Fraction *op2 = new Fraction(12, 5); Fraction op3 = *op1 + *op2; cout << op3 << endl; getchar(); return 1; }
注:当类操作符和全局操作符同时被重载时,类操作符被优先调用。
1.2.重载输入输出操作符
做输入输出表示时:重载的必须为非成员函数。
这个主要受第一个参数是istream或ostream的限制。
例子在上一个例子中的第二个中体现
通常情况下,输入输出运算符第一个形参时非常量的引用。因为流的输入会使状态改变。返回值是流的引用。
1.3.重载关系操作符
关系操作符:<,>.<=,>=,==,!=
重载关系运算符要和原有的关系运算符的思维相近
成语函数的写法:
bool operator == (const Object& obj) { }
非成语函数的写法:
bool operator == (const Object& obj1, const Object& obj2) { }
注:返回值是一个bool类型。如果定义了算术运算符,则一般也会定义个对应的复合复制运算符。
参数:可以重载(比如和一个固定的字符串相比)
1.4.重载下标运算符
[ ]用做访问一组元素中的某个元素。
也可称为:索引,唯一标识一个元素
下标运算必须是成语函数
成语函数的写法:
Element& operator [ ] (Type index) { }
参数:类型可以自己选择,用于指定元素下标
注:为了与普通下标元素兼容,返回值为元素的引用。一般会重载2个版本,一个返回是普通引用,另一个如果类是常量则返回常量引用。
1.5.重载递增递减运算符
递增递减运算符建议设计成成员函数
成语函数的写法:
Object& operator++() { }
返回值为元素的引用
可以加参数设计成区分前后置的,只需要在参数列表里加一个区分参数就可以
1.6.重载类型转换操作符()
转换构造函数和类型转换运算符共同定义了类类型转换。
类型转换运算符是类的一个特殊成语函数,他负责将一个类类型转换成其他类型。
一般形式如下:
operator Type( ) const { Type result; return result; }
类型运算符既没有显示的返回类型也没有形参,且必须定义成类的成员函数。
Type 类似于返回转化目标类型。除void外
不允许转换成数组或者函数类型,但允许转换成指针或引用类型。
1.7.函数调用运算符
如果类重载了函数调用运算符,则我们可以向使用函数一样使用该类的对象。
所以函数调用运算符必须是成员运算符。
举个例子:
struct absInt { int operator() (int val) const { return val < 0 ? -val : wal; } }; //使用: int i = -10; absInt absObj; int ui = absObj(i);
如果类定义了调用运算符,则该类的对象称作函数对象
2.模板
一个模板就是一个创建类或者函数的蓝图或者说公式
注:函数模板和类模板通常定义在头文件中。模板头文件应该包括模板的定义以及类模板或成员定义中用到的所有名字的声明。
2.1.函数模板
概念:一个函数模板就是一个公式,可以用来生活针对特定类型的函数版本
语法:
template <typename T>
T function(T a, T b)
{
T c;
...
return c;
}
模板参数列表是由一个逗号分隔一个或多个模板参数的列表,并用<>包围起来。
用模板类型可替代后面函数中出现所有的同名的类型。
注意:typename是关键字也可以用class代替,但为了不产生歧义,一般用typename
使用:
function <int>(val, 4);
在调用一个函数模板时,编译器会根据函数参数来自动推断模板参数,然后用推断的模板参数进行实例化成一个特定版本。
同时输入参数可以为常量,编译器也会自动转换。(这里还有个非模板类型参数的概念...感觉和动态识别常量有关先略过...)
函数模板用于实现通用算法,有人称之为泛型算法。
2.2.类模板
概念:类模板用来生成类的蓝图。
语法:
template <typename T>
class temp
{
}
调用方式:
temp<int> tmp;
temp<string> tmp2;
注:
1.一个模板类生成的每个实例都是一个独立的类
2.和函数模板不同的是,编译器不能为类模板推断模板参数类型
关于类模板名的使用:
下面是一个例子:
template <typename T1, typename T2> class temp { public: T1 typeTmp1; T2 typeTmp2; public: temp(T1 t1, T2 t2) //类内模板名和类内声明的写法相同 { typeTmp1 = t1; typeTmp2 = t2; } void display(ostream &os, T1 t1) { //类内模板名和类内声明的写法相同 os << "inner output!"; typeTmp1 = t1; } }; /* template <typename T1, typename T2> //类外模板名 temp<T1, T2>::temp(T1 t1, T2 t2) { typeTmp1 = t1; typeTmp2 = t2; } */ /* template <typename T1, typename T2> void temp<T1, T2>::display(ostream &os, T1 t1) //类外模板名 { os << "output!"; typeTmp1 = t1; } */ int main() { int i = 200; string j = "aaadaa"; temp<int, string> aal(i, j); temp<int, string> *p = new temp<int, string>(i, j); //new一个模板对象 aal.display(cout, i); getchar(); return 1; }上述中,类内模板名和类内声明相同,如果是类外则注意要加模板限制。
相关文章推荐
- [C/C++不常见语法特性]_[模板编译模式]
- 队列(queue) 之 c++模板(友元函数和运算符重载)
- c++ 的sort模板使用以及运算符重载
- 队列(queue) 之 c++模板实现(友元函数和运算符重载)
- C++--模板遇上运算符重载
- C++一些常见的运算符重载
- 智能指针:->和*运算符重载 + 模板技术 实现智能指针(C++)
- [C/C++不常见语法特性]_[模板编译模式]
- 【2016/3】C++ 类与对象进阶 运算符重载 new delete 模板 继承
- c++远征之模板篇——运算符重载
- C/C++编程细节(三)——类、继承、模板、运算符重载
- 【C++学习笔记】常见运算符重载
- 嵌入式开发之C++基础学习笔记5--静态成员,友元,运算符重载,模板,文件流
- 自己写的C++高精度模板(带运算符重载),与别人的对比
- 今天,我给大家介绍一些 c++常见头文件和模板,希望能给大家一些帮助!(第二次更新)
- dotNet中ArrayList的C++模板简单实现
- C++中的函数模板
- 混沌 IN C++::模板参数的奥秘
- C#中可以消除常见且浪费时间的C和C++错误的修改
- c++入门学习(函数模板)