在C++中,将一个对象赋给另外一个对象,编译器将提供赋值运算符的定义。
有两种情况,下面假设catman是Monster的一个实例
第一种:初始化
Monster golblen= catman;
第二种:普通赋值
Monster golblen;
golblen= catman;
复制构造函数
其中,第一种情况,系统将调用复制构造函数,其原型为
Monster(const Monster& monster);
如果Monster里没有提供该函数,编译器将自动提供,编译器自动提供时
Monster monster3 = monster1;
相当于
Monster monster3;
monster3. name = monster1.name
由于monster3的name实际上共享monster1的name,所以这种复制叫做“浅复制”,两个对象共享同一份成员变量,在析构时会报错。
解决错误的方式是自己定义一个复制构造函数,并且自己为name开辟空间,将monster1的name使用strcpy_s的方式拷贝到自己开辟的空间里
顺带一提,monster2是用new创建的,new出来的对象存活于堆中,其他两个对象存活于栈中,系统只会自动释放栈内空间,而堆内空间需要用户自己维护。
Monster类声明
#pragma once
#include <iostream>; using std::ostream;
using std::cin;
using std::cout;
using std::endl; class Monster { private:
char* name; public:
Monster();
Monster(const char* name);
Monster(const Monster& monster);
~Monster();
friend ostream& operator<<(ostream& os, const Monster& monster);
};
Monster类定义
#include "Monster.h"; Monster::Monster()
{
cout << "默认构造函数执行完毕" << endl;
} Monster::Monster(const char* name)
{
this->name = new char[strlen(name) + ];
strcpy_s(this->name, strlen(name) + , name);
cout << "构造函数执行完毕" << endl;
} //复制构造函数,如果没有定义,编译器将自动提供浅复制方式的复制构造函数
Monster::Monster(const Monster& monster)
{
this->name = new char[strlen(monster.name) + ];
strcpy_s(this->name, strlen(monster.name) + , monster.name);
cout << "复制构造函数执行完毕" << endl;
} Monster::~Monster()
{
cout << name << "has been destroyed." << endl;
delete[] name;
}
ostream& operator<<(ostream& os, const Monster& monster)
{
return os << monster.name;
} int main(void)
{
{
Monster monster1("Golblen");
cout << "monster1: " << monster1 << " online" << endl; //下面这种初始化方式,将会调用复制构造函数
Monster monster2 = monster1;
cout << "monster2: " << monster2 << " online" << endl; Monster monster3;
monster3 = monster1;
cout << "monster3: " << monster2 << " online" << endl;
}
cin.get();
return ;
}
运行结果
报错了,看来赋值的时候,并没有调用自定义的复制构造函数,所以释放name时出错了
赋值运算符
解决上面报错的问题,自需要增加一个成员函数,
函数原型
Monster& operator=(const Monster& monstere);
函数定义
Monster& Monster::operator=(const Monster& monster)
{
if (this == &monster)
{
return *this;
}
delete[] this->name;
int length = strlen(monster.name);
name = new char[length+];
strcpy_s(name, length + , monster.name);
cout << "赋值运算符调用完毕" << endl;
return *this;
}
定义赋值运算符时,必须要做的三件事情:
1.由于目标对象已经引用了之前分配的数据,所以一定要使用delete[]来释放这些数据,否则将出现内存泄漏
2.函数应当避免将对象赋给自身,否则做第1步操作释放数据时,会将对象的数据也释放掉
3.函数应当返回一个指向当前对象的调用
另外,赋值运算符只能由类成员函数重载,为什么呢 (见https://bbs.csdn.net/topics/392049284?page=1 )
1:对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)
2:对于所有楼主提到的操作符(=,[],(),->),只能声明为成员函数是为了避免不合法的书写通过编译(这是推测出的原因,更深层的可能要研究 C++ 的设计了)