在类和对象(一)中,简单介绍了一下关于类和对象的一些基础的知识点。了解到了oop的思想,并且对于类的特性以及访问限定符等有了简单的了解。
那么,在类中,用户不自己实现成员方法的时候,系统会给我们提供默认的函数,类中的6个默认的函数,本文将主要介绍类中的4个默认函数。
目录
一、类中的默六个认函数(默认函数时系统提供的函数,并且都是公有的,内联的。)
一、类中的默六个认函数(默认函数时系统提供的函数,并且都是公有的,内联的。)
1、构造函数
2、析构函数
3、拷贝构造函数
4、赋值运算符的重载函数
5、取地址运算符的重载函数
6、const修饰的取地址操作符的重载函数
那么,对于这六种默认的函数,我们进行深入的了解。
(一)构造函数
1、构造函数是用来初始化对象的内存空间的;
2、构造函数是可以重载的;
3、构造函数是系统调用函数;
4、构造函数不可以自己调用;
5、默认的构造函数,用户提供时,系统不会再提供;
6、构造函数构造完成时,对象生成;
7、默认的构造函数的调用方式:类名 对象;(切记:一定不能加写成“类名 对象();”);
(二)析构函数
1、析构函数是用来释放内存资源的;
2、析构函数不可以重载;
3、析构函数可以自己调用,但不建议使用;
由于析构函数自己调用时会退化成一个普通的成员方法,系统还会调用一次默认的析构函数。
4、析构函数调用前,对象存在;
5、默认的析构函数什么都不做;
6、默认的析构函数,用户提供时,系统不会再提供;
对于构造函数和析构函数,一个是初始化内存空间,一个是释放内存资源;这两个函数是一起存在的,并且先构造的对象后析构;系统提供的默认的构造函数和析构函数的原型为:(已Student类为依据)
Student()//构造函数
{
}
~Student()//析构函数
{
}
那么,我们以一个例子来看一下构造函数和析构函数,在例子中对两者进行区别和深入了解;
class Student
{
private:
int snum;//学号
char * sname;//姓名
int sage;//年龄
};
int main()
{
Student stu1;
Student stu2(1001,"wang",15);//出错
return 0;
}
创建一个Student类后,利用类声明一个对象,这时该对像默认调用系统所提供的默认构造函数和析构函数进行初始化该对象的内存空间以及最后释放内存资源等;并且当在类中这样声明一个对象:Student stu2(1001,"wang",15);则会被提示错误,如下:
当将stu1试图执行时,系统会提示如下的错误,也就是说当用户自己实现这些函数时,系统便不会再提供默认的函数。
同时,stu3能够运行成功,是因为对构造函数进行了重载。
![]()
我们可以自实现构造函数和析构函数,如下所示,同时给出构造函数的一个重载;
Student(int num,char *name,int age)
{
sname = new char[strlen(name) + 1]();
strcpy(sname,name);
snum = num;
sage = age;
}
Student(char *name)//构造函数可重载
{
sname = new char[strlen(name) + 1]();
strcpy(sname,name);
}
~Student()
{
delete sname;
sname = NULL;
}
(三)拷贝构造函数
1、函数原型: 类名 (const 类名& rhs)
2、用一个已存在的对象来生成一个相同类型的新对象;
3、实参传形参,一定要传引用,防止形参对象递归构造
系统提供的函数如下:Student(const Student& rhs)//系统提供的原型
{
snum = rhs.snum;
sname = rhs.sname;
sage = rhs.sage;
}
由此可见,系统提供的是浅拷贝的版本,带指针的浅拷贝的实现过程如下所示:

构造顺序:
stu2 (构造完成后,给stu2初始化内存空间)
stu4 (用stu2生成新的对象stu4时,利用系统提供的拷贝构造函数,即让stu4指向stu2的内存空间)
析构顺序:
stu4 (析构完成时,stu4指向的内存块被释放,即stu2的内存块)
stu2 (析构时,释放野指针,导致程序崩溃)
因此,在带有指针时,我们应该采用深拷贝的版本,带指针的深拷贝的过程如下:

构造顺序:
stu2(初始化自己的内存空间stu2)
stu4(调用拷贝构造函数,将stu2内存里的数据拷贝到stu4中用来初始化stu4的内存空间)
析构顺序:
stu4(释放stu4的内存资源,不影响stu2的使用)
stu2(正常释放自己的内存资源) s
因此,将上述浅拷贝的版本自实现为深拷贝的版本为:
Student(const Student& rhs)
{
std::cout << this << " :Student::Student(const Student&)" << std::endl;
sname = new char[strlen(rhs.sname) + 1]();
strcpy(sname,rhs.sname);
snum = rhs.snum;
sage = rhs.sage;
}
(四)赋值运算符的重载函数
1、函数原型: 类名& operator = (const 类名& rhs)
2、用一个已存在型的对象给另一已存在的对象赋值;
3、返回值:类类的引用
4、实现过程:自赋值
释放旧资源566
申请新资源
赋值
4、const的作用
防止修改实参
接收隐式生成的临时对象
浅拷贝版本,系统提供:
Student& operator = (const Student& rhs)
{
if(this == &rhs)
{
return *this;
}
snum = rhs.snum;
sname = rhs.sname;
sage = rhs.sage;
return *this;
}
深拷贝版本,用户自实现:
Student& operator = (const Student& rhs)
{
if(this != &rhs)
{
delete[] sname;
sname = new char[strlen(rhs.sname)+1]();
strcpy(sname ,rhs.sname);
snum = rhs.snum;
sage = rhs.sage;
return *this;
}
}
那么,在赋值运算符重载函数中为什么返回引用呢?
原因是:若返回无引用,即返回值为普通的类类型,则在连续赋值时,会生成临时量,存在将类类型赋给临时量的情况。在这里是不能将类类型赋值给临时量的。所以必须返回引用,杜绝这种情况的发生。
关于类中提供的这些默认的函数,需要注意以下的问题:
1、构造函数不依赖对象调用,也就是不能手动调用;
2、析构函数依赖对象调用,即就是可以手动调用;
3、拷贝构造函数是用一个已存在的对象对生成一个相同类型的新对象,实参传形参,一定要传引用,防止形参对象递归构造;
4、赋值运算符重载函数用一个已存在的对象来给另一个已存在的对象赋值。返回值是类类型的引用。
本文详细介绍了C++中类的六个默认函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、取地址运算符重载函数以及const修饰的取地址操作符重载函数。重点解析了构造函数、析构函数、拷贝构造函数和赋值运算符重载函数的特性和使用场景。

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



