1:赋值运算符函数
题目:如下类型为CMyString的声明,请为该类型添加赋值运算符函数。
class CMyString{ public: CMyString(char* pData=nullptr); CMyString(const CMyString& str); ~CMyString(void); private: char* m_pData; }
注意:
返回值类型是否声明为该类型的引用,并在函数结束前返回实例自身的引用(*this)。只有返回一个引用,才可以运行连续赋值。
是否把传入的参数类型声明为常量引用。如果传入的参数不是引用而是实例,那么从形参到实参会调用一次copy构造函数。
是否释放实例自身的内存。如果忘记在分配新内存之前释放自身已有的空间,则程序将出现内存泄漏。
判断传入的实参和当前的实例(*this)是不是同一个实例。如果是同一个,则不进行赋值操作,直接返回。如果事先不判断就进行赋值,那么再释放实例自身内存的时候就会导致严重的问题:当 *this和传入的参数是同一个实例时,一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此也找不到需要赋值的内容了了。
经典解法:
CMyString& CMyString::operator=(const CMyString&str){ if(str==&this) return *this; delete []m_pData; m_pData=nullptr; m_pData=new char[strlen(str.m_pData)+1]; strcpy(m_pData,str.m_pData); return *this; }
异常安全解法:
在前面的函数中,分配内存之前先用delete释放了实例m_pData的内存。如果此时内存不足导致new char抛出异常,则m_pData将是一个空指针,这样容易导致程序崩溃。
在赋值运算符函数中实现异常安全,方法一,先用new分配新内存,再用delete释放已有的内存;方法二,先创建一个临时的实例,再交换临时实例和原来的实例。
CMyString& CMyString::operator=(const CMyString& str) { if(thies!=&str) { CMyString strTemp(str); char* pTemp=strTemp.m_pData; strTemp.m_pData=m_pData; m_pData=pTemp; } return *this; }
2:Singleton模式
题目:设计一个类,我们只能生成该类的一个实例
懒汉式:
class singleton //实现单例模式的类 { private: singleton(){} //私有的构造函数 static singleton* Instance; public: static singleton* GetInstance() { if (Instance == NULL) //判断是否第一调用 Instance = new singleton(); return Instance; } };
改进的懒汉式:
class singleton //实现单例模式的类 { private: singleton(){} //私有的构造函数 static singleton* Instance; public: static singleton* GetInstance() { if (Instance == NULL) //判断是否第一调用 { Lock(); //表示上锁的函数 if (Instance == NULL) { Instance = new singleton(); } UnLock() //解锁函数 } return Instance; } };
饿汉式:
class singleton //实现单例模式的类 { private: singleton() {} //私有的构造函数 public: static singleton* GetInstance() { static singleton Instance; return &Instance; } };
3:数组中重复的数字
题目一:找出数组中重复的数字
在一个长度为n的数组里所有数字都在0~N-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道重复了几次。找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出应该是重复的数字2或者3。
bool duplicate(int numbers[], int length, int* duplication) { if(numbers == nullptr || length <= 0) //判断给定数组和长度是否符合要求 return false; for(int i = 0; i < length; ++i) //是否满足0~N-1的范围要求 { if(numbers[i] < 0 || numbers[i] > length - 1) return false; } for(int i = 0; i < length; ++i) { while(numbers[i] != i) { if(numbers[i] == numbers[numbers[i]]) { *duplication = numbers[i]; return true; } // 交换numbers[i]和numbers[numbers[i]] int temp = numbers[i]; numbers[i] = numbers[temp]; numbers[temp] = temp; } } return false; }
思路:依次遍历数组,做值与下标号比较,不相同则交换至该值对应的下标号处,如果下次需要交换的值所对应的下标号已经有等于该下标号的值,说明有重复的数字存在。
题目二:不能修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任一重复的数字,但不能改变输入的数组。
int getDuplication(const int* numbers, int length) { if(numbers == nullptr || length <= 0) return -1; int start = 1; int end = length - 1; while(end >= start) { int middle = ((end - start) >> 1) + start; int count = countRange(numbers, length, start, middle); if(end == start) { if(count > 1) return start; else break; } if(count > (middle - start + 1)) end = middle; else start = middle + 1; } return -1; } int countRange(const int* numbers, int length, int start, int end) { if(numbers == nullptr) return 0; int count = 0; for(int i = 0; i < length; i++) if(numbers[i] >= start && numbers[i] <= end) ++count; return count; }
思路:把从1~n的数字从中间的数字m分为两部分,前面一半为1~m,后面一半为m+1~n。如果后面一半为m+1~n。如果1~m的数字的数目超过m,那么这一半的区间里一定包含重复的数字;否则,另一半m+1~n的区间里一定包含重复的数字。可以继续把包含重复数字的区间一分为二,直到找到一个重复的数字。
4:二位数组中的查找
题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
bool Find(int target, vector<vector<int> > array) { int n=array.size();//行 int m=array[0].size(); int i=n-1,j=0; while(i>=0&&j<m) { if(array[i][j]==target) return 1; else if(array[i][j]<target) j++; else i--; } return 0; }