最近准备开搞算法竞赛,所以先回顾一下C++语法,发现之前看过的忘记了好多,一些交集性的东西居然看不懂了,赶紧恶补下。
由于是回顾,只对重点做些凌乱的记载。
还是喜欢以分析一套完整的代码来进行学习,翻翻资料,敲敲注释,不错不错:
#include<iostream> #include<cstdlib> using namespace std; enum ErrorType { invalidArraySize, memoryAllocationError, indexOutOfRange }; char *errorMsg[] = { "Invalid array size", "Memory allocation error", "Invalid index: " }; //类模板在之后声明定义中对类型的描述:Array<T> template <class T> //模板类的声明前必须加上template<class T>或者template<typename T>T所代表的是任意类型的名称,可以自定义 class Array //定以类名称,用于操作所存入的任意类型 { private: T* alist; //表示数组初识位置的指针 int size; //记录数组的大小 void Error(ErrorType error, int badIndex = 0) const; public: Array( int sz = 50 ); //构造函数 Array(const Array<T>& A); //拷贝构造函数 ~Array(); //析构函数 Array<T>& operator=( const Array<T>& rhs ); //对符号=的重载 T& operator[](int i); //对符号[]的重载 operator T*() const; //对指针转换符号的重载 int ListSize() const; //获得数组的大小 void Resize(int sz); //设置数组的大小 }; template<class T> void Array<T>::Error(ErrorType error, int badIndex) const { cout << errorMsg[error]; if(error == indexOutOfRange) cout << badIndex; cout << endl; exit(1); } template<class T> Array<T>::Array(int sz) //注意函数作用域的写法: 自定义模板类名<类型名>:: { if( sz <= 0) Error(invalidArraySize); size = sz; alist = new T[size]; if(alist == NULL) Error(memoryAllocationError); } template<class T> Array<T>::~Array() { delete [] alist; } template<class T> //模板类中的函数在类外实现时必须在声明前加上template<class T>(跟类声明一致),也就是定义为模板函数 Array<T>::Array(const Array<T>& X) //注意形参类型的表示方法:自定义模板类的类名<类的名称> 形参 ; 这里的形参声明为了const,无法修改形参 { int n = X.size; //这里拷贝构造函数的定义不是简单的浅拷贝(2个对象指向同一段内存),而是深拷贝,新建一段内存来存储新对象 size = n; alist = new T[n]; if(alist == NULL) Error(memoryAllocationError); T* srcptr = X.alist; T* destptr = alist; while(n--) *destptr++ = *srcptr++; } template<class T> Array<T>& Array<T>::operator=( const Array<T>& rhs) //这里运算符的重载和下面[]的重载有一个共同特点:返回值是引用类型 { //这是因为C++语法规定,如果函数的返回值是一个对象的值,它就被认为是一个常量 int n = rhs.size; //既然是常量,那就不能放在等号的左边,而[]和=都有可能出现在等号的左边,即左值 if(size != n) //所以这里的[],=就声明如此声明为成员函数。 这里大家可能会问,那为什么不声明成 { //友元函数呢?友元函数不是能很好的解决参数和符号的左右问题么?是的,但是C++语法 delete [] alist; //同时又规定"=" "[]" "()" "->" 只能重载为成员函数。。。因为安全性神马的吧 alist = new T[n]; if (alist == NULL) Error(memoryAllocationError); size = n; } T* destptr = alist; T* scrptr = rhs.alist; while(n--) *destptr++ = *scrptr++; return *this; } template<class T> T& Array<T>::operator[](int n) { if(n < 0 || n > size-1) Error(indexOutOfRange,n); return alist[n]; } template<class T> Array<T>::operator T*() const //指针转化函数的声明是必须的,这样可以让我们使用数组名等于数组首地址的基本功能 { //指针转换运算符的重载没有返回值类型,同时也没有形参类表,大家要注意噢 return alist; } template<class T> int Array<T>ListSize() const { return size; } template<class T> void Array<T>::Resize(int sz) { if(sz <= 0) Error(invalidArraySize); if(sz == size) return; T* newlist = new T[sz]; if(newlist == NULL) Error(memoryAllocationError); int n = (sz <= size) ? sz: size; T* srcptr = alist; alist = newlist; while(n--) *destptr++ = *srcptr++; delet [] alist; alist = newlist; size = sz; }
1.在类的成员函数中,若返回值为引用类型,起到的作用是对本对象的连续调用,例如<<,>>符号的重载返回值一般为引用类型。
2.在返回一个对象的时候,推荐创建无名的临时对象,也就是在return后紧跟 : 类名(构造函数所需的一些初始化参数);
3.将"++","--"重载为类的成员函数:
eg: Clock operator++(); // 前置单目运算符重载 A++
Clock operator++( int ); // 后置单目运算符重载 (唯一的区别是拥有int形参,但是不用形参名,只用来区分) ++A(返回值类型不能是引用)
4.Chopper前辈写的关于函数重载:http://www.cnblogs.com/wzh206/archive/2010/03/25/1696162.html
5.呼噜前辈对C++引用类型的介绍:http://www.cnblogs.com/wackelbh/archive/2009/12/29/1984064.html
6.运算符重载到底是选择声明为友元函数还是成员函数?
(1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
(2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
(3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
(4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
(5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
(6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。