完全弄懂C++中的构造与析构函数

类的构造函数
带参数的构造函数
使用初始化列表来初始化字段
类的析构函数
构造函数与析构函数的特点
显式调用析构函数
拷贝构造函数
类的构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。 推荐群:C++大学技术协会:145655849
下面的实例有助于更好地理解构造函数的概念:

#include

using namespace std;

class CLine
{
public:
void setLength(double len);
double getLength(void);
CLine(); // 这是构造函数

private:
double m_dblength;
};

// 成员函数定义,包括构造函数
CLine::CLine(void)
{
cout << “Object is being created” << endl;
}

void CLine::setLength(double len)
{
m_dblength = len;
}

double CLine::getLength(void)
{
return m_dblength;
}
// 程序的主函数
int main(int argc, char* argv[])
{
CLine line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;

system("pause");
return 0;

}
xxxxxxxxxx42 1#include 2​3using namespace std;4​5class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(); // 这是构造函数11​12private:13 double m_dblength;14};15​16// 成员函数定义,包括构造函数17CLine::CLine(void)18{19 cout << “Object is being created” << endl;20}21​22void CLine::setLength(double len)23{24 m_dblength = len;25}26​27double CLine::getLength(void)28{29 return m_dblength;30}31// 程序的主函数32int main(int argc, char* argv[])33{34 CLine line;35​36 // 设置长度37 line.setLength(6.0);38 cout << "Length of line : " << line.getLength() << endl;39​40 system(“pause”);41 return 0;42}

带参数的构造函数
默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:

#include

using namespace std;

class CLine
{
public:
void setLength(double len);
double getLength(void);
CLine(double len); // 这是构造函数

private:
double m_dblength;
};

// 成员函数定义,包括构造函数
CLine::CLine(double len)
{
cout << "Object is being created, length = " << len << endl;
m_dblength = len;
}

void CLine::setLength(double len)
{
m_dblength = len;
}

double CLine::getLength(void)
{
return m_dblength;
}
// 程序的主函数
int main(int argc, char* argv[])
{
CLine line(10.0);

// 获取默认设置的长度
cout << "Length of line : " << line.getLength() << endl;
// 再次设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;

system("pause");
return 0;

}
xxxxxxxxxx45 1#include 2​3using namespace std;4​5class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(double len); // 这是构造函数11​12private:13 double m_dblength;14};15​16// 成员函数定义,包括构造函数17CLine::CLine(double len)18{19 cout << "Object is being created, length = " << len << endl;20 m_dblength = len;21}22​23void CLine::setLength(double len)24{25 m_dblength = len;26}27​28double CLine::getLength(void)29{30 return m_dblength;31}32// 程序的主函数33int main(int argc, char* argv[])34{35 CLine line(10.0);36​37 // 获取默认设置的长度38 cout << "Length of line : " << line.getLength() << endl;39 // 再次设置长度40 line.setLength(6.0);41 cout << "Length of line : " << line.getLength() << endl;42​43 system(“pause”);44 return 0;45}

使用初始化列表来初始化字段
使用初始化列表来初始化字段:

CLine::CLine( double len): m_dblength(len)
{
cout << "Object is being created, length = " << len << endl;
}
xxxxxxxxxx4 1CLine::CLine( double len): m_dblength(len)2{3 cout << "Object is being created, length = " << len << endl;4}

上面的语法等同于如下语法:

CLine::CLine( double len)
{
m_dblength = len;
cout << "Object is being created, length = " << len << endl;
}
xxxxxxxxxx5 1CLine::CLine( double len)2{3 m_dblength = len;4 cout << "Object is being created, length = " << len << endl;5}

假设有一个类 CVolume,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:

CVolume::CVolume( double a, double b, double c): X(a), Y(b), Z©
{

}
xxxxxxxxxx4 1CVolume::CVolume( double a, double b, double c): X(a), Y(b), Z©2{3 …4}

类的析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
下面的实例有助于更好地理解析构函数的概念:

#include

using namespace std;

class CLine
{
public:
void setLength(double len);
double getLength(void);
CLine(); // 这是构造函数声明
~CLine(); // 这是析构函数声明

private:
double m_dblength;
};

// 成员函数定义,包括构造函数
CLine::CLine(void)
{
cout << “Object is being created” << endl;
}
CLine::~CLine(void)
{
cout << “Object is being deleted” << endl;
}

void CLine::setLength(double len)
{
m_dblength = len;
}

double CLine::getLength(void)
{
return m_dblength;
}
// 程序的主函数
int main()
{
CLine line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;

//system("pause");
return 0;

}
xxxxxxxxxx47 1#include 2​3using namespace std;4​5class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(); // 这是构造函数声明11 ~CLine(); // 这是析构函数声明12​13private:14 double m_dblength;15};16​17// 成员函数定义,包括构造函数18CLine::CLine(void)19{20 cout << “Object is being created” << endl;21}22CLine::~CLine(void)23{24 cout << “Object is being deleted” << endl;25}26​27void CLine::setLength(double len)28{29 m_dblength = len;30}31​32double CLine::getLength(void)33{34 return m_dblength;35}36// 程序的主函数37int main()38{39 CLine line;40​41 // 设置长度42 line.setLength(6.0);43 cout << "Length of line : " << line.getLength() << endl;44​45 //system(“pause”);46 return 0;47}

构造函数与析构函数的特点
对于构造函数而言:

与类同名
没有返回值
会在变量的生命周期开始时被调用
构造函数不能被显示调用
可以重载
对于析构函数而言:

名称为~类名()
没有返回值
会在变量的生命周期结束时调用
可以显式调用(一般不这样做)
不能重载
关于自动调用时机的总结:

全局变量:构造函数在main之前调用,析构函数在main之后调用
局部变量:构造函数在进去函数开始时调用,析构函数在return之前调用
静态局部变量:构造函数在第一次进入函数时调用,析构函数在main之后调用
堆中的变量:构造和析构的调用时机由程序员自己控制,与new和delete什么时候被调用有关
默认构造:当需要构造函数(如有成员是对象,或者继承关系),而类中有没有实现构造函数。编译器会自动实现一个默认构造。
默认析构:同理,有需要时,编译器会偷偷实现一个默认析构,用于调用基类及成员的析构。

显式调用析构函数
显式调用的时候,析构函数相当于的一个普通的成员函数
编译器隐式调用析构函数,如分配了堆内存,显式调用析构的话引起重复释放堆内存的异常
把一个对象看作占用了部分栈内存,占用了部分堆内存(如果申请了的话),这样便于理解这个问题。系统隐式调用析构函数的时候,会加入释放栈内存的动作(而堆内存则由用户手工的释放)。用户显式调用析构函数的时候,只是单纯执行析构函数内的语句,不会释放栈内存,摧毁对象。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

通过使用另一个同类型的对象来初始化新创建的对象。
复制对象把它作为参数传递给函数。
复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:

classname(const classname &obj)
{
//构造函数的主体
}
xxxxxxxxxx4 1classname(const classname &obj)2{3 //构造函数的主体4}

在此,obj是一个对象引用,该对象是用于初始化另一个对象的。

#include

using namespace std;

class CLine
{
public:
int getLength(void);
CLine(int len); // 简单的构造函数
CLine(const CLine &obj); // 拷贝构造函数
~CLine(); // 析构函数

private:
int* ptr;
};

// 成员函数定义,包括构造函数
CLine::CLine(int len)
{
cout << “调用构造函数” << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}

CLine::CLine(const CLine &obj)
{
cout << “调用拷贝构造函数并为指针 ptr 分配内存” << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}

CLine::~CLine(void)
{
cout << “释放内存” << endl;
delete ptr;
}
int CLine::getLength(void)
{
return *ptr;
}

void display(CLine obj)
{
cout << "line 大小 : " << obj.getLength() << endl;
}

// 程序的主函数
int main(int argc, char* argv[])
{
CLine line(10);

display(line);

return 0;

}
xxxxxxxxxx56 1#include 2​3using namespace std;4​5class CLine6{7public:8 int getLength(void);9 CLine(int len); // 简单的构造函数10 CLine(const CLine &obj); // 拷贝构造函数11 ~CLine(); // 析构函数12​13private:14 int* ptr;15};16​17// 成员函数定义,包括构造函数18CLine::CLine(int len)19{20 cout << “调用构造函数” << endl;21 // 为指针分配内存22 ptr = new int;23 *ptr = len;24}25​26CLine::CLine(const CLine &obj)27{28 cout << “调用拷贝构造函数并为指针 ptr 分配内存” << endl;29 ptr = new int;30 *ptr = *obj.ptr; // 拷贝值31}32​33CLine::~CLine(void)34{35 cout << “释放内存” << endl;36 delete ptr;37}38int CLine::getLength(void)39{40 return ptr;41}42​43void display(CLine obj)44{45 cout << "line 大小 : " << obj.getLength() << endl;46}47​48// 程序的主函数49int main(int argc, char argv[])50{51 CLine line(10);52​53 display(line);54​55 return 0;56}

下面的实例对上面的实例稍作修改,通过使用已有的同类型的对象来初始化新创建的对象:

#include

using namespace std;

class CLine
{
public:
int getLength(void);
CLine(int len); // 简单的构造函数
CLine(const CLine &obj); // 拷贝构造函数
~CLine(); // 析构函数

private:
int *ptr;
};

// 成员函数定义,包括构造函数
CLine::CLine(int len)
{
cout << “调用构造函数” << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}

CLine::CLine(const CLine &obj)
{
cout << “调用拷贝构造函数并为指针 ptr 分配内存” << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}

CLine::~CLine(void)
{
cout << “释放内存” << endl;
delete ptr;
}
int CLine::getLength(void)
{
return *ptr;
}

void display(CLine obj)
{
cout << "line 大小 : " << obj.getLength() << endl;
}

// 程序的主函数
int main(int argc, char* argv[])
{
CLine line1(10);

CLine line2 = line1; // 这里也调用了拷贝构造函数

display(line1);
display(line2);

return 0;

}
​x 1#include 2​3using namespace std;4​5class CLine6{7public:8 int getLength(void);9 CLine(int len); // 简单的构造函数10 CLine(const CLine &obj); // 拷贝构造函数11 ~CLine(); // 析构函数12​13private:14 int *ptr;15};16​17// 成员函数定义,包括构造函数18CLine::CLine(int len)19{20 cout << “调用构造函数” << endl;21 // 为指针分配内存22 ptr = new int;23 *ptr = len;24}25​26CLine::CLine(const CLine &obj)27{28 cout << “调用拷贝构造函数并为指针 ptr 分配内存” << endl;29 ptr = new int;30 *ptr = *obj.ptr; // 拷贝值31}32​33CLine::~CLine(void)34{35 cout << “释放内存” << endl;36 delete ptr;37}38int CLine::getLength(void)39{40 return ptr;41}42​43void display(CLine obj)44{45 cout << "line 大小 : " << obj.getLength() << endl;46}47​48// 程序的主函数49int main(int argc, char argv[])50{51 CLine line1(10);52​53 CLine line2 = line1; // 这里也调用了拷贝构造函数54​55 display(line1);56 display(line2);57​58 return 0;59}

拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用拷贝构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用拷贝构造函数。
C++支持两种初始化形式:
拷贝初始化 int a = 5; 和直接初始化 int a(5); 对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数,也就是说:

CMyclass x(2);  //直接初始化,调用构造函数
CMyclass y = x;  //拷贝初始化,调用拷贝构造函数
1CMyclass x(2);  //直接初始化,调用构造函数2CMyclass y = x;  //拷贝初始化,调用拷贝构造函数

必须定义拷贝构造函数的情况:
只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义拷贝构造函数也可以拷贝;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义拷贝构造函数。
什么情况使用拷贝构造函数:
类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:

一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。
推荐群:C++大学技术协会:145655849

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值