选用网课:中国大学MOOC平台,北大的程序设计与算法(三)C++面向对象程序设计
教师:郭炜
时间:2019.8.11
目标:加深对于C++中面向对象的理解
1.基本概念理解加深
-运算符重载的实质是函数的重载(;不只是狭义上的+ - * /等)
-优先重载为成员函数,在需要全局调用时,考虑友元函数
-把含运算符的表达式转换为对成员函数的调用来理解(a-b等价于a.operator+等)操作数转换为函数参数
2.赋值运算符的重载
使得赋值运算符两边类型可以不匹配,=只能重载为成员函数。为了保持一致性,=的返回值为左值的引用
如果左值正是该类,即可return *this;
注意浅复制和深复制的区别尤其是有Pointer时
eg.S1=S2,此时s1指针也指向了同一片区域,然而s1原来所指向区域成为了内存垃圾。
3.运算符重载为友元函数
我们希望,比如+,不仅可以c+5,也可以5+c
4.可变长数组的一个实例
这里注意 []
的重载返回值,根据具体需求,有时是int&有时是int*
-还有一个是,要注意区分复制构造函数的浅复制坑,默认的复制构造函数只会完成成员的复制,当成员是指针时便容易发生问题
//这里的一个常规操作是,先划区域,再strcpy
//同时注意防止s=s问题
if(ptr==a.ptr)
return *this;
else
ptr=new int[strlen(a.ptr)+1];
strcpy(ptr,a.ptr);
5.流插入和流输入运算符
cout是在iostream中定义的,ostream类的对象
<<
本身是左移运算符,在iostream中得到了重载
对于我们的需求,需要将<<重载为全局函数,又可以访问类的私有成员,友元函数最佳。返回值仍需要为ostream对象,以满足连续输出。
eg.
friend ostream & operator<<(ostream &o, A &a){
o<
注意这里的ostream &o
中,去掉&会报错,老师也没细讲原因。
6.类型转换运算符的重载
operator double(){...}
肯定无参数,无返回值类型
(int)s
等价于s.int()
在一些自定义类隐式转换中可以很好地解决问题。
7.自增自减运算符重载
为了区分++i与i++,C++规定后置运算符,需要多一个无用的int变量,以示区分。
-还有要注意的是,返回值不同。前置返回自增完的对象,后置返回自增前的备份(temp)
T & operator++()//++i
{n++;return *this;}
T operator++(int)//i++
{T tmp(*this);//备份
n++;
return tmp;}
几点注意
重载不改变优先级
.
,.*
,::
,?:
,sizeof
不能被重载
()、[]、->、=只能重载为成员函数
程序填空练习题
1.MyString
这题比较常规,关于赋值号的重载,还有个流输出重载,因为不涉及连续赋值(形如a=b=c),返回值可以设为void
但为了养成习惯,保持优雅,最好返回值设为MyString &
#include
#include
#include
using namespace std;
class MyString {
char * p;
public:
MyString(const char * s) {
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
~MyString() { if(p) delete [] p; }
//begin of my code
MyString(MyString &s){
p=new char[strlen(s.p)+1];strcpy(p,s.p);
}
void operator=(const char * s) {
if(s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
void operator=(const MyString &s) {
if(s.p) {
p = new char[strlen(s.p) + 1];
strcpy(p,s.p);
}
else
p = NULL;
}
friend ostream &operator<<(ostream &o,const MyString &s){
o<> w1 >> w2) {
MyString s1(w1),s2 = s1;
MyString s3(NULL);
s3.Copy(w1);
cout << s1 << "," << s2 << "," << s3 << endl;
s2 = w2;
s3 = s2;
s1 = s3;
cout << s1 << "," << s2 << "," << s3 << endl;
}
}
2.看上去好坑的运算符重载
这道题初次分析卡了会儿,以为是输出的n-5,n-7后来明白了Inc只是在输出时+1并没有改变,是输出n-5,n-8。
另一个点,注意到Inc()的参数是Int,而主函数中的调用时Myint型
我的初次解决方案是,在类中写了个Inc的重载函数
后来发现,可以直接写一个类型转换重载函数,重载int(),使主函数调用Inc是,隐式调用(int)可以通过。
#include
using namespace std;
class MyInt
{
int nVal;
public:
MyInt( int n) { nVal = n ;}
//Begin of my code
MyInt & operator-(const int &a){
nVal-=a;return *this;
}
friend int Inc(MyInt &a){
return a.nVal+1;
}
};
int Inc(int n) {
return n + 1;
//End of my code
}
int main () {
int n;
while(cin >>n) {
MyInt objInt(n);
objInt-2-1-3;
cout << Inc(objInt);
cout <<",";
objInt-2-1;
cout << Inc(objInt) << endl;
}
return 0;
}
上面的友元函数可以代替为
operator int()
{
return nVal;
}
4.二维数组类
这题卡了会儿,一开始的方案就是动态分配二维数组,但之前没有在类内存储行和列,一直卡在怎样通过动态分配的二维数组来获得行和列数。后来屈服了,在类内存储了行和列,一切自然解决了。
这里还考察了括号的重载,[]的重载。
网上关于数组存储,也可以使用静态分配的方法。那么可以用sizeof来判断行和列。
#include
#include
using namespace std;
class Array2 {
//Begin of my code
int **num;
int col;int row;
public:
Array2(int a,int b):row(a),col(b){
num=new int*[a];
for(int i=0;i
5.别叫,这个大整数已经很简化了!
这道题乍看确实感觉挺麻烦的,需要自己做大数加法重载,判断进位等等。
参考了篇资料,将大数反向按字节存储,整型同样处理可以解决问题。
#include
#include
#include
#include
using namespace std;
const int MAX = 110;
class CHugeInt {
private:
char maxNum[210];
int len;
public:
CHugeInt(char * s){
strcpy(maxNum,s);
int i=0,j=strlen(s)-1;
while(i=10)
{
temps.maxNum[i]=sum%10+'0';
//cout<=temps.len)
{
temps.maxNum[i+1]+='0';
}
flag=1;
}else{
//cout<<"sum:"<=len)
{
if(flag==1){
temps.maxNum[i+1]='\0';
temps.len=i+1;
}
else{
temps.maxNum[i]='\0';
temps.len=i;
}
}
return temps;
}
/*operator char *()
{
return maxNum;
}*/
CHugeInt & operator +=(int n)
{
CHugeInt temps(n);
*this=this->operator+(temps);
//cout<maxNum<=i;j--)
os<maxNum<operator +=(1);
//cout<> s >> n) {
CHugeInt a(s);
CHugeInt b(n);
cout << a + b << endl;
cout << n + a << endl;
cout << a + n << endl;
b += n;
cout << ++ b << endl;
cout << b++ << endl;
cout << b << endl;
}
return 0;
}