类的构造函数
带参数的构造函数
使用初始化列表来初始化字段
类的析构函数
构造函数与析构函数的特点
显式调用析构函数
拷贝构造函数
类的构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 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 23using namespace std;45class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(); // 这是构造函数1112private:13 double m_dblength;14};1516// 成员函数定义,包括构造函数17CLine::CLine(void)18{19 cout << “Object is being created” << endl;20}2122void CLine::setLength(double len)23{24 m_dblength = len;25}2627double CLine::getLength(void)28{29 return m_dblength;30}31// 程序的主函数32int main(int argc, char* argv[])33{34 CLine line;3536 // 设置长度37 line.setLength(6.0);38 cout << "Length of line : " << line.getLength() << endl;3940 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 23using namespace std;45class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(double len); // 这是构造函数1112private:13 double m_dblength;14};1516// 成员函数定义,包括构造函数17CLine::CLine(double len)18{19 cout << "Object is being created, length = " << len << endl;20 m_dblength = len;21}2223void CLine::setLength(double len)24{25 m_dblength = len;26}2728double CLine::getLength(void)29{30 return m_dblength;31}32// 程序的主函数33int main(int argc, char* argv[])34{35 CLine line(10.0);3637 // 获取默认设置的长度38 cout << "Length of line : " << line.getLength() << endl;39 // 再次设置长度40 line.setLength(6.0);41 cout << "Length of line : " << line.getLength() << endl;4243 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 23using namespace std;45class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(); // 这是构造函数声明11 ~CLine(); // 这是析构函数声明1213private:14 double m_dblength;15};1617// 成员函数定义,包括构造函数18CLine::CLine(void)19{20 cout << “Object is being created” << endl;21}22CLine::~CLine(void)23{24 cout << “Object is being deleted” << endl;25}2627void CLine::setLength(double len)28{29 m_dblength = len;30}3132double CLine::getLength(void)33{34 return m_dblength;35}36// 程序的主函数37int main()38{39 CLine line;4041 // 设置长度42 line.setLength(6.0);43 cout << "Length of line : " << line.getLength() << endl;4445 //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 23using namespace std;45class CLine6{7public:8 int getLength(void);9 CLine(int len); // 简单的构造函数10 CLine(const CLine &obj); // 拷贝构造函数11 ~CLine(); // 析构函数1213private:14 int* ptr;15};1617// 成员函数定义,包括构造函数18CLine::CLine(int len)19{20 cout << “调用构造函数” << endl;21 // 为指针分配内存22 ptr = new int;23 *ptr = len;24}2526CLine::CLine(const CLine &obj)27{28 cout << “调用拷贝构造函数并为指针 ptr 分配内存” << endl;29 ptr = new int;30 *ptr = *obj.ptr; // 拷贝值31}3233CLine::~CLine(void)34{35 cout << “释放内存” << endl;36 delete ptr;37}38int CLine::getLength(void)39{40 return ptr;41}4243void display(CLine obj)44{45 cout << "line 大小 : " << obj.getLength() << endl;46}4748// 程序的主函数49int main(int argc, char argv[])50{51 CLine line(10);5253 display(line);5455 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 23using namespace std;45class CLine6{7public:8 int getLength(void);9 CLine(int len); // 简单的构造函数10 CLine(const CLine &obj); // 拷贝构造函数11 ~CLine(); // 析构函数1213private:14 int *ptr;15};1617// 成员函数定义,包括构造函数18CLine::CLine(int len)19{20 cout << “调用构造函数” << endl;21 // 为指针分配内存22 ptr = new int;23 *ptr = len;24}2526CLine::CLine(const CLine &obj)27{28 cout << “调用拷贝构造函数并为指针 ptr 分配内存” << endl;29 ptr = new int;30 *ptr = *obj.ptr; // 拷贝值31}3233CLine::~CLine(void)34{35 cout << “释放内存” << endl;36 delete ptr;37}38int CLine::getLength(void)39{40 return ptr;41}4243void display(CLine obj)44{45 cout << "line 大小 : " << obj.getLength() << endl;46}4748// 程序的主函数49int main(int argc, char argv[])50{51 CLine line1(10);5253 CLine line2 = line1; // 这里也调用了拷贝构造函数5455 display(line1);56 display(line2);5758 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