在Mayuyu讲解指针悬挂之前,先来简单讲一下运算符重载,因为一般情况下我们是通过重载赋值运算符来解决指针
悬挂问题的。
我们知道,在C++中,编译时多态性是通过函数重载和运算符重载来实现的,也称为静态多态性。而运行时多态性是
通过继承和虚函数来实现的,也称为动态多态性。那么我们先来看看什么是运算符重载。
我们都知道对于像"+","-","*","/",这样的运算符来说,进行运算的对象必须是基本数据类型,比如int,long
等等。那么两个对象将无法进行这些操作,实际上通过运算符重载,同样可以进行这些操作,比如两个复数相加。
我们可以写出如下代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
class complex
{
public:
double real;
double imag;
complex(double real=0.0,double imag=0.0)
{
this->real = real;
this->imag = imag;
}
};
complex operator+(complex c1,complex c2)
{
complex t;
t.real = c1.real + c2.real;
t.imag = c1.imag + c2.imag;
return t;
}
//重载输出运算符"<<"
ostream &operator<<(ostream &out,complex &c)
{
out<<c.real<<"+"<<c.imag<<"i"<<endl;
return out;
}
int main()
{
complex c1(1.0,2.0);
complex c2(3.0,4.0);
complex ans1,ans2;
ans1 = c1 + c2;
ans2 = operator+(c1,c2);
cout<<ans1<<ans2<<endl;
return 0;
}
在我们定义好重载运算符后,可以通过ans1 = c1 + c2或者ans2 = operator+(c1,c2)来使用它。似乎上面
的代码没有什么问题,但是前提是我们把数据成员都定义成共有的,所以在外界我们能直接用,如果你把它们设置
private类型的,你会发现编译不过,原因很简单:私有数据在类外部不能访问。我们学过友元函数,他本身不是
类的成员函数,但是我们却可以通过它来访问类内部的私有数据,相当于在类这个无法进入的黑匣子开了一个小孔,
这样就使得外界可以访问类内部的私有数据了。
我们把上面的代码修改一下:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
class complex
{
private:
double real;
double imag;
public:
complex(double real=0.0,double imag=0.0)
{
this->real = real;
this->imag = imag;
}
friend complex operator+(complex c1,complex c2);
friend ostream &operator<<(ostream &out,complex &c);
};
complex operator+(complex c1,complex c2)
{
complex t;
t.real = c1.real + c2.real;
t.imag = c1.imag + c2.imag;
return t;
}
//重载输出运算符"<<"
ostream &operator<<(ostream &out,complex &c)
{
out<<c.real<<"+"<<c.imag<<"i"<<endl;
return out;
}
int main()
{
complex c1(1.0,2.0);
complex c2(3.0,4.0);
complex ans1,ans2;
ans1 = c1 + c2;
ans2 = operator+(c1,c2);
cout<<ans1<<ans2<<endl;
return 0;
}
嗯,这样达到了目的。。。注意不能用友元函数重载的运算符有:=,(),[],->,所以要重载这四个运算符,必
须把它们当作成员函数。
现在我们来看两个重要的运算符++p和p++,它们是怎么重载的呢 ? 这个很重要。。。在C++中,编译器可以通过
在运算符函数参数表中是否插入关键字int来区分这两种方式。即:
classname operator++(); 相当于++p
classname operator++(int); 相当于p++
比如:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
class X
{
private:
int x,y;
public:
X(int x,int y)
{
this->x = x;
this->y = y;
}
X operator++();
X operator++(int);
friend ostream &operator<<(ostream &out,X &c);
};
X X::operator++()
{
++x;
++y;
return *this;
}
X X::operator++(int)
{
x++;
y++;
return *this;
}
//重载输出运算符"<<"
ostream &operator<<(ostream &out,X &c)
{
out<<c.x<<" "<<c.y<<endl;
return out;
}
int main()
{
X c1(1,2);
X c2(3,4);
++c1;
c2++;
cout<<c1<<c2<<endl;
return 0;
}
到了这里,Mayuyu把运算符重载基本讲完了。。。
讲完运算符重载,我们来进一步看看指针悬挂问题。
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
class String
{
private:
char *str;
public:
String(char *s)
{
str = new char[strlen(s) + 1];
strcpy(str,s);
}
~String()
{
delete str;
}
void Print()
{
cout<<str<<endl;
}
};
int main()
{
String p1("Mayuyu");
{
String p2("Hello!");
p2 = p1;
p2.Print();
}
p1.Print();
return 0;
}
如上代码,我们知道对象之间的赋值其实就是引用,而不是重新开辟一块新的空间。也就是说在main()中执行语句
p2 = p1后,指针指向情况如下图:
也就是说,p1和p2指向同一块内存空间,而在主函数的{}中的代码在执行完毕后会调用p2的析构函数,也就是说
p1和p2共同指向的那块内存被释放了,那么将会出现指针p1指向不明确,p1变成了野指针。这样是极其危险的。
所以我们要解决这个问题,一个很明显的解决办法就是,在执行"="运算符时,让p1和p2各指向一个内存块,而这
两个内存块一个是另一个的复制品,这样就避免了上面的出错情况。当然实际上还有一个做法也是比较好的,方法
大致如下:
对于同一个内存块,我们用一个count统计,有多少个指针指向它,加一个指针count++,释放一个count--,
如果最后count = 1时,我们才真正释放这块内存,这样貌似比较麻烦,不在我们讨论范围内。
下面,我们只需要重载"=",在函数体内实现拷贝就行了。代码如下:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
class String
{
private:
char *str;
public:
String(char *s)
{
str = new char[strlen(s) + 1];
strcpy(str,s);
}
~String()
{
delete str;
}
void Print()
{
cout<<str<<endl;
}
String &operator=(const String &);
};
String &String::operator=(const String &p)
{
if(this == &p) return *this;
delete str;
str = new char[strlen(p.str) + 1];
strcpy(str,p.str);
return *this;
}
int main()
{
String p1("Mayuyu");
{
String p2("Hello!");
p2 = p1;
p2.Print();
}
p1.Print();
return 0;
}
这样,就解决了指针悬挂问题。。。
Mayuyu在这里感谢广大网友的阅读,不要忘了评论哦!!!