先来复习一下C++的内存分区问题~
C++内存分区
C++程序的内存分区可划分为四大内存分区:堆、栈、全局/静态存储区和代码区。 不同类型的变量存放的区域不同。
在程序被编译为exe可执行文件后,未运行之前,分为两个区域:代码区和全局区。
代码区
存放程序体的二进制代码。比如我们写的函数,都是在代码区的。
int a = 0;//静态全局变量区
char *p1; //编译器默认初始化为NULL
void main(){
int b; //栈
char s[] = "abc";//栈
char *p2 = "123456";//123456在字符串常量区,p2在栈上
static int c =0; //c在静态变量区,0为文字常量,在代码区
const int d=0; //栈
static const int d;//静态常量区
p1 = (char *)malloc(10);//分配得来得10字节在堆区。
strcpy(p1, "123456"); //123456放在字符串常量区,编译器可能会将它与p2所指向的"123456"优化成一个地方
}
以上所有代码,编译成二进制后存放于代码区,文字常量存放于代码区,是不可寻址的。
全局区
全局区存储全局变量,静态变量,还包括了常量区,字符串常量和其他常量(const修饰的全局变量)也存放在全局区。
全局区的数据在程序结束后由操作系统释放。
程序运行之后
栈区
由编译器自动释放,存放函数的参数值,局部变量等。
整个程序的栈区的大小可以在编译器中由用户自行设定。
注意:不要返回局部变量地址。
堆区
由编程人员手动申请,手动释放,若不手动释放,程序结束后由系统回收,生命周期是整个程序运行期间。
使用new进行堆的申请,堆的总大小为机器的虚拟内存的大小。new返回的是该数据类型的指针。
int * p = new int(10);
使用delete进行释放。
重载方式有两种:
1.重载的函数作为成员函数。
2.重载的函数作为全局函数
函数名是规定的:operator+
加号运算符(“+”)重载
对于内置的数据类型,编译器知道如何进行运算。
两个自定义的数据类型,进行相加,就需要进行加号运算符重载。
成员函数实现加号重载:
class Person{
public:
Person operator+(Person &p){
Person tmp;
tmp.a = this->a + p.a;
tmp.b = this->b + p.b;
return tmp;
}
int a;
int b;
};
int main(){
Person A;
A.a = 10;
A.b = 20;
Person B;
B.a = 1;
B.b = 3;
Person C;
C = A + B;
cout << C.a << " " << C.b << endl;
}
运行结果:
全局函数实现加号重载:
class Person{
public:
int a;
int b;
};
Person operator+(Person &p1,Person &p2){ //需要传入两个参数
Person tmp;
tmp.a = p1.a + p2.a;
tmp.b = p1.b + p2.b;
return tmp;
}
int main(){
Person A;
A.a = 10;
A.b = 20;
Person B;
B.a = 1;
B.b = 3;
Person C;
C = A + B;
cout << C.a << " " << C.b << endl;
}
运行结果:
运算符重载也可以发生函数重载
class Person{
public:
int a;
int b;
};
Person operator+(Person &p1,Person &p2){
Person tmp;
tmp.a = p1.a + p2.a;
tmp.b = p1.b + p2.b;
return tmp;
}
Person operator+(Person &p1,int n){ //函数重载
Person tmp;
tmp.a = p1.a + n;
tmp.b = p1.b + n;
return tmp;
}
int main(){
Person A;
A.a = 10;
A.b = 20;
Person B;
B.a = 1;
B.b = 3;
Person C;
C = A + B;
cout << C.a << " " << C.b << endl;
Person D;
D = A + 10;
cout << D.a << " " << D.b << endl;
}
运行结果:
左移运算符(“<<”)符重载
可以输出自定义的数据类型。
class Person{
public:
int a;
int b;
};
ostream& operator<<(ostream &cout , Person &p){
cout << p.a << " " << p.b << endl;
return cout; //返回cout 可以实现链式编程
}
int main(){
Person A;
A.a = 10;
A.b = 20;
cout << A << endl;
}
运行结果:
左移运算符重载无法用成员函数来实现
因为要实现的是cout << A << endl;
看例子吧~
#include <iostream>
using namespace std;
#include <string>
class Person{
public:
int a;
int b;
ostream& operator<<(ostream &cout){
cout << this->a << " " << this->b << endl;
return cout;
}
};
int main(){
Person A;
A.a = 10;
A.b = 20;
A << cout << endl; //如果用成员函数,最后实现了是这样的。。。(因为是A调用,所以A写在前)
}
运行结果:
递增运算符(“++”)重载
class Person{
public:
int a;
int b;
Person& operator++(){ //前置递增返回的是引用,这样++(++A)也不会出错
a++;
b++;
return *this;
}
//后置递增返回的是值
Person operator++(int){ //int是占位参数,可以用来区分前置和后置,必须写int,其他类型不可以
Person tmp = *this;
a++;
b++;
return tmp;
}
};
ostream& operator<<(ostream &cout , Person &p){
cout << p.a << " " << p.b << endl;
return cout;
}
int main(){
Person A;
A.a = 10;
A.b = 20;
cout << "原始A的值:" <<A << endl;
++A;
cout << "A++后的值:" << A << endl;
A++;
cout << "A++后的值:" << A << endl;
}
运行结果:
赋值运算符(“=”)重载
存在复制运算符的原因是堆区的数据重复释放,也就是浅拷贝的问题。
详细解释:
在对象P1中new一个age变量,这个遍历被存储在堆区,需要程序员手动申请,手动释放,把P1赋给p2时,P2也指向这个堆区的地址空间,所以当使用析构函数对堆区数据进行释放时,P1释放了一次,P2又释放了一次,出现了堆区数据重复释放的情况,程序崩溃。
解决方案:
利用深拷贝来解决浅拷贝的问题。
因为出现程序崩溃的主要原因是,当p2 = p1时,p2直接指向了0x0011这块地址,使用赋值运算符重载,使p2 = p1时,p2开辟一个新的地址空间用来存储年龄18这个数据,这样在释放时,就不会重复释放了。
class Person {
public:
Person(int age) {
this->age = new int(age);
}
int *age;
~Person() {
if (age != NULL) {
delete age;
age = NULL;
}
}
Person &operator=(Person &p) { //返回类型是Person,可以链式赋值,p3 = p2 = p1
if (this->age != NULL) {
delete age;
age = NULL;
}
age = new int(*p.age);
return *this;
}
};
int main() {
Person p1(10);
cout << *p1.age << endl; //p1.age是指针
Person p2(18);
cout << *p2.age << endl;
p2 = p1;
cout << *p2.age << endl;
Person p3(20);
cout << *p3.age << endl;
p1 = p2 = p3;
cout << *p1.age << " " << *p2.age << " " << *p3.age << endl;
}
运行结果:
关系运算符重载
public:
Person(string name, int age,int sex) {
this->name = name;
this->age = age;
this->sex = sex;
}
string name;
int age;
int sex;
bool operator==(Person &p){
return this->name == p.name && this->age == p.age && this->sex == p.sex;
}
bool operator!=(Person &p){
return !(this->name == p.name && this->age == p.age && this->sex == p.sex);
}
};
int main() {
Person p1("Tom",23,1);
Person p2("Tom",23,1);
Person p3("Amy",23,2);
cout << (p1 == p2 ? "相等" : "不相等")<< endl;
cout << (p2 == p3 ? "相等" : "不相等")<< endl;
cout << (p1 != p2 ? "不相等" : "相等")<< endl;
cout << (p2 != p3 ? "不相等" : "相等")<< endl;
}
运行结果:
函数调用运算符重载
class Person {
public:
void operator()(int b) {
cout << b << endl;
}
};
int main() {
Person p1;
p1.operator()(6);
// 匿名函数对象
Person()(9);
}
运行结果:
补充: