C++-学习(2)------基本语法介绍(模板和运算符重载)

126 阅读2分钟

####函数模板 C++ 中的模板分为函数模板和类模板两种 C++中,很多函数要求能够处理许多不同类型的数据,比如比较大小的函数,既要能够比较整数也要求能够比较字符串,这些算法处理的数据是不相同的,但是算法本身是相同的,这种情况下就可以使用函数模板来简化工作 示例代码:

template <typename T> 
inline T const&  Max(T const& a, T const& b) {

	return a > b ? a : b;
}

T 表示一种抽象的数据类型,template 也可以写成 template ,译器会自动根据我们传入的标识符类型,来推导出相应的函数

调用形式:显示实例化或者隐式实例化(推导)

	//test function teamplate
	{
		cout << "Max uint32_t ret = " << Max(10, 20) << endl;
		//cout << "Max uint32_t ret = " << Max<uint32_t>(10, 20) << endl;

		cout << "Max float ret = " << Max(1.0f, 2.1f) << endl;
		//cout << "Max uint32_t ret = " << Max<float>(1.0f, 2.1f) << endl;

		string s1 = "hello";
		string s2 = "world";
		//cout << "Max string ret = " << Max(s1, s2) << endl;
		cout << "Max uint32_t ret = " << Max<string>(s1, s2) << endl;
	}

####模板类 下面的代码定义了一个stack 的模板类,类中使用vector 存储数据

template <typename T>  //template <class T> is also ok
class Stack {
public:
	Stack(uint32_t size);
	void push(T const&);
	T pop();
	T top();
	bool empty();
private:
	vector<T> mvec;
};

template <typename T>
Stack<T>::Stack(uint32_t size) {
	mvec.reserve(size);
}

template <typename T>
void Stack<T>::push(T const& a) {
	mvec.push_back(a);
}

template <typename T>
T Stack<T>::pop() {
	auto p = mvec.back();
	mvec.pop_back();
	return p;
}

template <typename T>
T Stack<T>::top() {
	auto p = mvec.back();
	return p;
}

template <typename T>
bool Stack<T>::empty() {
	if (mvec.size() == 0) {
		return true;
	} else {
		return false;
	}
}

注意点:

  • 定义模板类中的成员函数时,需要加上模板定义的开头 template
  • 模板类中的成员函数,用**Stack**指定类名

隐式实例化的调用形式:

Stack<string> mystack(10);
mystack.push("pushA");
mystack.push("pushB");
mystack.push("pushC");
mystack.push("pushD");

string p = mystack.pop();
cout << "mysatck pop element: " << p << endl;
cout << "mysatck pop element: " << mystack.top() << endl;
cout << "mysatck empty: " << mystack.empty() << endl;

#####模板类的显式实例化 通常情况下,模板类的定义和实现是放在一起的,在.h和.cpp 文件中,当然也可以将它们像普通类一样分开放; 因为模板类、函数通常定义在头文件中,这些头文件会被很多cpp文件包含,在这些cpp文件中会多次使用这些模板;编译完后的可执行程序中会包含多份模板类的定义,然而实际上,整个程序中却只有一份模板类的定义,这个处理是在编译和链接过程中实现的,目前主流的实现模式有两种

  • Borland模式(先实例化模板类,再剔除)
  • Cfront模式(存储在模板存储库) g++实现的是Borland 模式,由于我们为每一份实例化生成代码,这样在大型程序中就有可能包含很多重复的实例化定义代码,虽然链接阶段,链接器会剔除这些重复的定义,但仍然会导致编译过程中的目标文件(或者共享库文件)过于庞大。这时候,我们就可以通过C++11的模板显式实例化的方法解决

下面定义Stack 模板类,实现是在 templateDemo.cpp 中

#include <stdint.h>
#include <iostream>
#include "vector"
#include "templateDemo.h"

template class Stack<uint32_t>;  //显式实例化
template class Stack<string>; //显式实例化


/*   template stack demo    */
template <typename T>
Stack<T>::Stack(uint32_t size) {
	mvec.reserve(size);
}

template <typename T>
void Stack<T>::push(T const& a) {
	mvec.push_back(a);
}

template <typename T>
T Stack<T>::pop() {
	auto p = mvec.back();
	mvec.pop_back();
	return p;
}

template <typename T>
T Stack<T>::top() {
	auto p = mvec.back();
	return p;
}

template <typename T>
bool Stack<T>::empty() {
	if (mvec.size() == 0) {
		return true;
	}
	else {
		return false;
	}
}

templateDemo.h 定义模版类Stack

#ifndef _TEMPLATEDEMO_
#define _TEMPLATEDEMO_

#include <string>
#include <vector>

using namespace std;

// template function Max
template <typename T>
inline T const&  Max(T const& a, T const& b) {

	return a > b ? a : b;
}


// template function templateprint
template <typename T>
void templateprint(T& a) {
	cout << "demo print value: " << a << endl;
}


// templaye class Stack
template <typename T>  //template <class T> is also ok
class Stack {
public:
	Stack(uint32_t size);
	void push(T const&);
	T pop();
	T top();
	bool empty();
private:
	vector<T> mvec;
};
#endif

在templateDemo.cpp 显式实例化 Stack 类模板,在其他文件中就可以很自由的include templateDemo.h;当然也可以将模板类Stack 定义实现放在一起,可以避免使用显示实例化;

####C++ 运算符重载 运算符重载的本质还是函数重载,可以将运算符理解为特殊的函数名;

  • 运算符重载时,运算顺序和优先级不变,操作数个数不变
  • 不能创造新的操作符,并且规定下面的六个操作符不能重载
操作符说明
.类属关系操作符
.*成员指针操作符
::作用域操作符
?:条件运算操作符
#预编译处理符号
sizeof取数据长度操作符

可以重载的运算符基本可以分为:

  • 赋值运算符 =
  • 关系运算符 > 、< 、>= 、==、!=、<=
  • 算术运算符 +、++、–、- -、+=、-=、/、*
  • 其他特殊的运算符 <<、>>、[ ]、() 定义一个演示操作符重载的类:
class complexDemoClass {
public:
	inline complexDemoClass(double a, double b) :r(a), t(b) {
		cout << "construct complexDemoClass (a,b)" << endl;
	}

	inline complexDemoClass() {
		cout << "construct complexDemoClass nopara" << endl;
	}

	inline ~complexDemoClass() {
		cout << "destruct ~complexDemoClass" << endl;
	}
	
	...... // define all operator function
	inline void dumpClass() {
		cout << "dumpClass r = " << r << " t = " << t << endl;
	}
private:
	double r;
	double t;	
}

######赋值运算符重载 基本形式为:模板形式为 classname& operator=(classname& 实例对象) 注意输入参数只有一个,而且必须是对象的引用,返回值也必须是对象的引用 tips:可以将operator= 看成一个整体,当成函数名

complexDemoClass& operator=(complexDemoClass& p) {
	//cout << "Run" << __FUNCTION__ << endl;
	r = p.r;
	t = p.t;
	return *this;
}

调用形式如下,注意传递的是引用,所以不可以将指针作为参数传入

complexDemoClass democlass(1.0f, 1.0f);
complexDemoClass democlassb(1.3f, 1.3f);
democlass = democlassb;
//democlass.dumpClass();
complexDemoClass* pclassc = new complexDemoClass(2.0f, 2.0f);

//democlass = *pclassc;
//democlass.dumpClass();

######算术运算符重载 算术运算符包括+、++、–、- -、+=、-=、/、*等,基本模板形式为: classname& operator运算符(classname& 实例对象) 除了自增和自减运算符,其余算术运算符都是一个参数,必须是对象的引用,返回值也是对象的引用

只要语句中使用到了下面的算术运算符都会触发运算符重载函数,如果要接收返回值,可以再定义一个引用,又或者定义一个相应的类型实例去接收,此时实际调用到了对应类型拷贝构造函数

下面的运算符可以直接重载,重载函数可以不属于任何一个类,下面两种格式都是支持的:

  • 全局运算符重载 friend classname& operator运算符(const classname& a,const classname& b)
  • 成员函数重载 classname& operator运算符(const classname& b)
//有点疑问?是将栈上的变量返回
inline complexDemoClass& operator+(complexDemoClass& p) {
	 return complexDemoClass(r + p.r, t + p.t);
}
inline complexDemoClass& operator-(complexDemoClass& p) {
	return complexDemoClass(r - p.r, t - p.t);
}
inline complexDemoClass& operator*(complexDemoClass& p) {
	return complexDemoClass(r*p.r, t*p.t);
}
inline complexDemoClass& operator/(complexDemoClass& p) {
	return complexDemoClass(r / p.r, t / p.t);
}

inline complexDemoClass& operator+=(complexDemoClass& p) {
	r += p.r;
	t += p.t;
	return *this;
}
inline complexDemoClass& operator-=(complexDemoClass& p) {
	r -= p.r;
	t -= p.t;
	return *this;
}
inline complexDemoClass& operator++(void) {
	r += 1.0f;
	t += 1.0f;
	return *this;
}
inline complexDemoClass& operator--(void) {
	r -= 1.0f;
	t -= 1.0f;
	return *this;
}

注意算术运算符+,-,*,/ 的调用形式如下 不可以使用 complexDemoClass democ; democ = demoa + demob;的形式,此形式又会调用到赋值运算符重载

complexDemoClass democ 此时已经实例化了对象

complexDemoClass demoa(1.0f, 1.0f);
complexDemoClass demob(2.0f, 2.0f);

complexDemoClass democ = demoa + demob;
democ.dumpClass();

complexDemoClass demod = demoa - demob;
demod.dumpClass();

complexDemoClass demoe = demoa * demob;
demoe.dumpClass();

complexDemoClass demof = demoa / demob;
demof.dumpClass();

+,-,*,/ 重载函数调用完成之后,两个输入对象本身没有变化

+=,-= 运算符重载的定义和调用形式为:

inline complexDemoClass& operator+=(complexDemoClass& p) {
	r += p.r;
	t += p.t;
	return *this;
}
inline complexDemoClass& operator-=(complexDemoClass& p) {
	r -= p.r;
	t -= p.t;
	return *this;
}
调用形式:
{
	complexDemoClass demoa(1.0f, 1.0f);
	complexDemoClass demob(2.0f, 2.0f);
	demoa += demob;
	//demoa.dumpClass();

	demoa -= demob;
	//demoa.dumpClass();
}

注意使用 +=,-= 需要定义在类的内部,本质上对象的属性本身被操作符改变了

++ -- 运算符重载(TBD)

#####关系运算符重载 基本模板形式为:

  • 全局运算符重载 friend bool& operator运算符(const classname& a,const classname& b)
  • 成员函数重载 bool& operator运算符(const classname& a)

成员函数重载实例:

inline bool operator>(const complexDemoClass& a) {
	bool ret = (r > a.r) && (t > a.t);
	return ret;
}

inline bool operator<(const complexDemoClass& a) {
	bool ret = (r < a.r) && (t < a.t);
	return ret;
}

inline bool operator==(const complexDemoClass& a) {
	bool ret = (r == a.r) && (t == a.t);
	return ret;
}


inline bool operator!=(const complexDemoClass& a) {
	bool ret = (r != a.r) && (t != a.t);
	return ret;

}	

调用形式为:

{
	complexDemoClass demoa(1.0f, 1.0f);
	complexDemoClass demob(2.0f, 2.0f);

	bool ret = (demoa == demob);
	cout << "demoa == demob:" << ret << endl;

	ret = (demoa != demob);
	cout << "demoa != demob:" << ret << endl;

	cout << "demoa > demob:" << (demoa > demob) << endl;
	cout << "demoa < demob:" << (demoa < demob) << endl;
}