2021-09-01

标题c++的三大函数

1、为什么有构造函数
类的数据成员不能在类的声明时候初始化,为了解决这个问题,使用构造函数处理对对象的初始化。构造函数是一种特殊的成员函数,与其他函数不同,不需要用户调用它,而是创建对象的时候自动调用。
2、构造函数特点:
(1)、函数名与类名相同。
(2)、函数无返回值。
(3)、创建对象时系统自动调用。
(4)、构造函数可以有多个,创建对象时根据传入的参数不同调用不同的构造函数。也就是可以重载。

如下代码:
class Clock
{
public:
Clock(int NewH,int NewM,int NewS); //有参数的构造函数
Clock() //无参数的构造函数
{
hour = 0;
minute = 0;
second = 0;
}
void SetTime(int NewH,int NewM,int NewS);
void ShowTime();

private:
int hour,minute,second;
};

int main()
{
Clock(0,0,0); //调用有参数的构造函数
Clock my_clock; //调用无参数的构造函数
return 0;
}

析构函数:
1、析构函数特点:
(1)、函数名:~类名。
(2)、函数无返回值。
(3)、当对象生存周期结束或者调用delete()清理时,由系统自动调用。
(4)、无参,不可重载。

2、构造函数和析构函数的调用顺序:
1、优先调用基类的构造函数,如果多个,按照继承的顺序从左到右。
2、调用子对象的构造函数,如果有多个,按照声明的顺序从上往下。
3、调用派生类自己的构造函数。
4、析构函数调用顺序与构造函数相反。

拷贝构造函数
1、什么是拷贝构造函数

首先对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=100;
int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
下面看一个类对象拷贝的简单例子。
#include
using namespace std;
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a=b;
printf(“constructor is called\n”);
}
//拷贝构造函数
CExample(const CExample & c)
{
a=c.a;
printf(“copy constructor is called\n”);
}
//析构函数
~CExample()
{
cout<<“destructor is called\n”;
}
void Show()
{
cout<<a<<endl;
}
};
int main()
{
CExample A(100);
CExample B=A;
B.Show();
return 0;
}
运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。

2、拷贝构造函数的调用时机

(1)、当函数的参数为类的对象时
#include
using namespace std;
class CExample
{
private:
int a;
public:
CExample(int b)
{
a=b;
printf(“constructor is called\n”);
}
CExample(const CExample & c)
{
a=c.a;
printf(“copy constructor is called\n”);
}
~CExample()
{
cout<<“destructor is called\n”;
}
void Show()
{
cout<<a<<endl;
}
};
void g_fun(CExample c)
{
cout<<“g_func”<<endl;
}
int main()
{
CExample A(100);
CExample B=A;
B.Show();
g_fun(A);
return 0;
}
调用g_fun()时,会产生以下几个重要步骤:
(1).A对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把A的值给C。 整个这两个步骤有点像:CExample C(A);
(3).等g_fun()执行完后, 析构掉 C 对象。

(2)、函数的返回值是类的对象
#include
using namespace std;
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a=b;
printf(“constructor is called\n”);
}
//拷贝构造函数
CExample(const CExample & c)
{
a=c.a;
printf(“copy constructor is called\n”);
}
//析构函数
~CExample()
{
cout<<“destructor is called\n”;
}
void Show()
{
cout<<a<<endl;
}
};
CExample g_fun()
{
CExample temp(0);
return temp;
}
int main()
{

g_fun();
return 0;

}
当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_fun()执行完后再析构掉XXXX对象。

(3)、对象需要通过另外一个对象进行初始化
CExample A(100);
CExample B=A;

3.1深拷贝和浅拷贝
看下面一段代码
#include
using namespace std;
class Rect
{
public:
Rect()
{
count++;
}
~Rect()
{
count–;
}
static int getCount()
{
return count;
}
private:
int width;
int height;
static int count;
};
int Rect::count=0;
int main()
{
Rect rect1;
cout<<“The count of Rect:”<<Rect::getCount()<<endl;
Rect rect2(rect1);
cout<<“The count of Rect:”<<Rect::getCount()<<endl;
return 0;
}
在主函数中,首先创建对象rect1,输出此时的对象个数,然后使用rect1复制出对象rect2,再输出此时的对象个数,按照理解,此时应该有两个对象存在,但实际程序运行时,输出的都是1,反应出只有1个对象。此外,在销毁对象时,由于会调用销毁两个对象,类的析构函数会调用两次,此时的计数器将变为负数。

如下图所示(浅拷贝,只是将成员的值进行赋值)

出现这些问题最根本就在于在复制对象时,计数器没有递增,我们重新编写拷贝构造函数,如下:
#include
using namespace std;
class Rect
{
public:
Rect()
{
count++;
}
Rect(const Rect& r)
{
width=r.width;
height=r.height;
count++;
}
~Rect()
{
count–;
}
static int getCount()
{
return count;
}
private:
int width;
int height;
static int count;
};
int Rect::count=0;
int main()
{
Rect rect1;
cout<<“The count of Rect:”<<Rect::getCount()<<endl;
Rect rect2(rect1);
cout<<“The count of Rect:”<<Rect::getCount()<<endl;
return 0;
}
如下图所示:

总结深拷贝和浅拷贝:

(1)浅拷贝:就是在对象赋值时,只是对对象中的数据成员进行简单的赋值,如果对象中存在动态成员,即指针,释放内存时,同一段内存地址会被释放两次,程序会报错,并且浅拷贝的对象,任何一方的变动都会影响到另一方。
(2)深拷贝:针对成员变量存在指针的情况,不仅仅是赋值成员的值,而是重新分配内存空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值