C++运算符重载 模板友元 new delete ++ = +=
今天的重载是基于C++ 类模板的,如果需要非类模板的重载的朋友可以把类模板拿掉,同样可以参考,谢谢。
一、类模板中的友元重载
本人喜好类声明与类成员实现分开写的代码风格,如若您喜欢将类成员函数的实现写在类声明中,那么可以跳过该部分。
请看下面这段代码:
头文件:
#pragma once template<typename T> class CLA { T m_value; public: CLA(const T&); friend CLA operator+(const CLA&, const CLA&); }; template<typename T> CLA<T>::CLA(const T& a) :m_value(a) { } template<typename T> CLA<T> operator+(const CLA<T>& lhs, const CLA<T>& rhs) { return CLA<T>(lhs.m_value + rhs.m_value); }
源文件:(已包含上述的头文件)
int main() { CLA<int> a{ 0 }, b{ 1 }, c{ 2 }; a + b; return 0; }
我们去执行上述代码的时候,编译器就会报错:一个无法解析的外部指令。
当然,将实现放入声明中是可以的,但是为了维护类的书写风格,我们还是希望有一种方法可以去维护这个风格。
那么我们可以将类中友元函数的声明写成如下形式:
friend CLA operator+<T>(const CLA&, const CLA&);
原因很简单,类模板具有抽象性,而刚刚那个友元函数就是普通的函数,不具有模板的抽象性。
即使参数为CLA<T> ... 还是一样,它代表的只不过是一个参数的类型,函数本身依旧是一个普通的 函数。
而上述的形式更像一个函数模板,将函数的模板实参同步于类模板的参数,这样就可以作为类模板的友元了。
二、各种运算符重载
这部分我们将会说到 + - * / 关系运算符 赋值 自增自减 以及new delete 的重载。
首先,几个简单的 + - * / 友元以及非友元重载形式
#pragma once #include<iostream> using namespace std; template<typename T> class CLA { T m_value; public: CLA():m_value(0){} CLA(const T&); CLA(const CLA&); //友元形式 friend CLA operator+<T>(const CLA&, const CLA&); //同类型 friend CLA operator+<T>(const CLA&, const T); //不同类型 friend CLA operator-<T>(const CLA&, const CLA&); //同类型 friend CLA operator-<T>(const CLA&, const T); //不同类型 //非友元形式 CLA operator*(const CLA&); //同类型 CLA operator*(const T); //不同类型 CLA operator/(const CLA&); //同类型 CLA operator/(const T); //不同类型 }; template<typename T> CLA<T>::CLA(const T& a) :m_value(a) { } template<typename T> CLA<T> operator+(const CLA<T>& lhs, const CLA<T>& rhs) { return CLA<T>(lhs.m_value + rhs.m_value); } template<typename T> CLA<T> operator+(const CLA<T>& lhs, const T rhs) { return CLA<T>(lhs.m_value + rhs); } template<typename T> CLA<T>::CLA(const CLA<T>& rhs) : m_value(rhs.m_value) { } template<typename T> CLA<T> operator-(const CLA<T>& lhs, const CLA<T>& rhs) { return CLA<T>(lhs.m_value - rhs.m_value); } template<typename T> CLA<T> operator-(const CLA<T>& lhs, const T rhs) { return CLA<T>(lhs.m_value - rhs); } template<typename T> CLA<T> CLA<T>::operator*(const CLA<T>& rhs) { return CLA<T>(m_value * rhs.m_value); } template<typename T> CLA<T> CLA<T>::operator*(const T rhs) { return CLA<T>(m_value * rhs); } template<typename T> CLA<T> CLA<T>::operator/(const CLA<T>& rhs) { if (rhs.m_value) return CLA<T>(m_value / rhs.m_value); else cout << "非法除法!" << endl; return CLA<T>(); } template<typename T> CLA<T> CLA<T>::operator/(const T rhs) { if (rhs) return CLA<T>(lhs.m_value / rhs); else cout << "非法除法!" << endl; return CLA<T>(); }
接下来重载输入输出流,而且必须为友元才行
friend istream& operator>> <T>(istream&, CLA&); friend ostream& operator<< <T>(ostream&, const CLA&);
istream& operator>>(istream& is, CLA<T>& rhs) { is >> rhs.m_value; return is; } template<typename T> ostream& operator<<(ostream& os, const CLA<T>& rhs) { os << rhs.m_value; return os; }
输入流重载的第二个参数不能为const,因为在函数体中要对之进行修改
然后,我们用下面的代码来看一下测试结果:
int main() { CLA<int> a{ 0 }, b{ 1 }, c{ 2 }; CLA<char> B{ 'b' }; b / a; //相同类型 cout << b*c << endl; //相同类型 cout << B + (char)1 << endl; //不同类型的 cout << B - (char)1 << endl; //不同类型的
return 0; }
没有问题
接下类重载一些赋值运算符,= += -=
CLA& operator=(const CLA&); CLA& operator+=(const CLA&); CLA& operator-=(const CLA&);
CLA<T>& CLA<T>::operator=(const CLA<T>& rhs) { if (this != &rhs) m_value = rhs.m_value; return *this; } template<typename T> CLA<T>& CLA<T>::operator+=(const CLA<T>& rhs) { m_value += rhs.m_value; return *this; } template<typename T> CLA<T>& CLA<T>::operator-=(const CLA<T>& rhs) { m_value -= rhs.m_value; return *this; }
赋值号要记得要有判同语句,运算完成后要将*this,也就是操作符的左操作数,返回。
这部分之后进行测试
++重载(--雷同)
CLA& operator++(); //前++ const CLA operator++(int); //后++
CLA<T>& CLA<T>::operator++() { ++m_value; return *this; } template<typename T> const CLA<T> CLA<T>::operator++(int) //语法规定,在后++函数的参数中加int以作区分 { CLA<T> item(m_value); ++m_value; return item; }
根据前++和后++的语法规则,前++,将本身的值+1,然后再将其本身返回。如上述操作函数体语句所示。
而后++则是将原值返回,然后本身+1,所以,我们需要借助一个局部变量来保存原值,而且返回之后是不允许改变的,代表一个常量,所以返回值拥有const属性
关系运算符也挺简单的
friend bool operator!= <T>(const CLA&, const CLA&);//友元 bool operator!=(const CLA&); //成员函数 bool operator==(const CLA&); bool operator<(const CLA&); bool operator>=(const CLA&);
template<typename T> bool operator!=(const CLA<T>& lhs, const CLA<T>& rhs) { return lhs.m_value != rhs.m_value; } bool CLA<T>::operator!=(const CLA<T> & rhs) { return this->m_value != rhs.m_value; } template<typename T> bool CLA<T>::operator==(const CLA<T>& rhs) { return m_value == rhs.m_value; } template<typename T> bool CLA<T>::operator<(const CLA<T>& rhs) { return m_value < rhs.m_value; } template<typename T> bool CLA<T>::operator>=(const CLA<T>& rhs) { return m_value > rhs.m_value; }
我们来测试一下
int main() { CLA<int> a{ 0 }, b{ 1 }, c{ 2 }; if (a != b) cout << "a!=b" << endl; a++; if (a == b) cout << "a==b" << endl; //c++++ //Invalid !! CLA<int> r = ++++a; cout << r << endl; cout << (r += 1) << endl; cout << (r < a) << endl; return 0; }
忘了写连续赋值,不过,经测试也是可以的,没问题 。
那么,现在我们来重载 new 和delete 以及new [ ] 和 delete [ ]
void* operator new(size_t size); void* operator new[](size_t size); void operator delete(void* p); void operator delete[](void* p);
这里我们要用到的一个C语言库里面的类型—— size_t,它是unsigned int,sizeof运算符算出来的值就是它喽,在这里作为参数,它会自动计算大小,很方便
那,我们来看一下它的实现:
template<typename T> void* CLA<T>::operator new(size_t size) { cout << size << endl; cout << "调用了new" << endl; return malloc(size); } template<typename T> void* CLA<T>::operator new[](size_t size) { cout << size << endl; cout << "调用了new[]" << endl; return malloc(size); } template<typename T> void CLA<T>::operator delete(void* p) { free(p); cout << "调用了delete函数" << endl; } template<typename T> void CLA<T>::operator delete[](void* p) { free(p); cout << "调用了delete[]函数" << endl; }
我们写一些对应的输出来帮助我们确定一些信息。
int main() { CLA<int>* w = new CLA<int>(10); delete w; CLA<int>* W = new CLA<int>[10]; delete[] W; return 0; }
第一个10为创建的对象的值,第二个为开辟的数组的大小
一个int为4个字节,开辟10个大小的内存,为40个字节,没有问题
类模板的运算符重载就到这里了
感谢您的阅读,祝您生活愉快!