类中有六个默认函数:
1、构造函数
2、析构函数
3、拷贝构造函数
4、赋值运算符重载函数
5、取地址操作符的重载函数
6、const修饰的取地址操作符的重载函数
以下是简单实现了一个商品CGoods的类,类中是基本的4个成员函数:
class CGoods
{
public:
CGoods()
{}
~CGoods()
{}
CGoods(const CGoods& rhs);
CGoods& operator=(const CGoods& rhs);
private:
char* mname;
float mprice;
int mamount;
};
1、构造函数
对象的生成一般分为两步:
1、系统开辟内存
2、系统调用构造函数给内存做初始化
构造函数:函数名 = 类名,作用是给开辟的内存做初始化。由于类的成员方法依赖对象调用,构造完成后才生成对象,所以构造函数无法手动调用,只能系统调用。
构造函数可以自己写,也可以使用系统默认的构造函数。若用户提供构造函数,系统就不会提供;若没有,则使用系统提供的构造函数。由此可见,构造函数可以重载。
CGoods()//系统提供的默认构造函数
{}
CGoods(char* name, float price, int amount)//用户自定义的构造函数
{
mname = new char[strlen(name) + 1];
strcpy(mname, name);
mprice = price;
mamount = amount;
}
构造函数的使用:
CGoods good1;//使用默认的构造函数,只是生成一个对象
CGoods good2("rice",2,100);//使用自定义的构造函数,并进行初始化
CGoods good3();//函数声明,并没有生成对象
2、析构函数
对象的销毁一般也分为两步:
1、调用析构函数释放对象所占用的其他资源;
2、释放内存
析构函数:函数名 = ~类名,作用是释放对象所占用的其他资源。由于析构函数调用前对象已经存在,所以析构函数可以手动调用,手动调用析构函数时,析构函数会退化成一个普通成员方法,之后系统还会再调用一次析构函数。
析构函数可以自己写。若用户提供析构函数,则使用用户提供的;若没有,则使用系统默认的析构函数。由于资源与内存只能释放一次,所以析构函数不支持重载
~CGoods()//系统默认的构造函数
{}
~CGoods()//用户自定义构造函数
{
delete[] mname;
std::cout << this << "CGoods::~CGoods()" << std::endl;
mname = NULL;
}
3、拷贝构造函数
拷贝构造函数:函数名 = 类名,作用是拿一个已存在的对象来生成一个相同类型的新对象。
CGoods good1("car1", 100000, 10);//生成对象good1
CGoods good2 = good1;//用已存在的对象good1来生成对象good2
//相当于CGoods good2(good1);
默认的拷贝构造函数是浅拷贝,只是实现了简单的数值的拷贝,若有指针类型存在,会造成两个对象的指针指向同一个内存块,导致同一个内存块重复释放,结果出错。
CGoods(const CGoods& rhs)
{
mname = rhs.mname;
mprice = rhs.mprice;
mamount = rhs.mamount;
}
若类中有指针类型,则考虑实现深拷贝,使每个指针都指向各自的内存块,再进行拷贝赋值,避免内存的重复释放。
CGoods(const CGoods& rhs)
{
mname = new char[strlen(rhs.mname) + 1];
strcpy(mname, rhs.mname);
mprice = rhs.mprice;
mamount = rhs.mamount;
}
拷贝构造函数 CGoods(const CGoods & rhs)中,& 是必须的吗

由上图可见,若没有 &引用符号,拷贝构造函数将生成 rhs 形参对象,由于生成形参对象的过程中也调用拷贝构造函数,函数会递归陷入死循环,形参对象无法生成。有 &引用符号时,引用只是给对象起别名,不会生成新的对象。

4、赋值运算符重载函数
赋值运算符重载函数:函数名 = operator= ,作用是拿一个已存在的对象给另一个已存在的对象赋值。
CGoods good1("car1", 100000, 10);//构造生成一个对象
CGoods good3;//构造生成另一个对象对象
good3 = good1;//用一个已存在对象给另一个已存在对象赋值
默认的拷贝构造函数是浅拷贝,只是实现了简单的数值的拷贝,若有指针类型存在,会造成两个对象的指针指向同一个内存块,导致同一个内存块重复释放,结果出错。
CGoods operator=(const CGoods& rhs)
{
if (this == &rhs)//自赋值判断,防止出现 good1 = good1 的情况
{
return *this;
}
mname = rhs.mname;
mprice = rhs.mprice;
mamount = rhs.mamount;
return *this;
}
由于该类中含有指针类型,浅拷贝会导致两个指针指向同一块内存,对象析构时重复释放出错,所以要考虑实现深拷贝:
CGoods& operator=(const CGoods& rhs)//CGoods* const this
{
if (this != &rhs)
{
delete[] mname;
mname = new char[strlen(rhs.mname) + 1];
strcpy(mname, rhs.mname);
mprice = rhs.mprice;
mamount = rhs.mamount;
}
return *this;
}
由此可见,赋值运算符重载函数的实现有4步:
1、自赋值判断(判断是否自己给自己赋值)
2、释放旧资源
3、开辟新资源
4、赋值
赋值运算符重载函数中 CGoods& operator=(const CGoods& rhs) 形参的 & 是必须的吗

赋值运算符重载函数中的&引用不是必须的,没有的话会生成一个临时对象,多了两个函数的调用:构造、析构。有的话不会生成临时对象,免去这过程,更高效。
本文介绍了C++类中的四个默认函数:构造函数、析构函数、拷贝构造函数和赋值运算符重载函数。详细阐述了它们的作用、使用场景以及注意事项,如构造函数和析构函数的调用时机,拷贝构造函数和赋值运算符重载函数的深拷贝与浅拷贝问题。

被折叠的 条评论
为什么被折叠?



