(⼀⼀六)类的构造函数和析构函数
类构造函数:
构造函数 是专门⽤于构造新对象、将值赋给它们的数据成员。
C++为这些成员提供了名称和使⽤语法,⽽程序员需要提供⽅法定义。名称与类名相同。
例如:Stock类的⼀个可能的构造函数是名为Stock()的成员函数。
构造函数的原型和函数头有⼀个有趣的特征——虽然没有返回值,但没有被声明为void类型。实际上,构造函数没有声明类型。
声明和定义构造函数:
和使⽤普通函数(根据传递的参数给私有成员赋值)⼏乎⼀样,除了构造函数的函数名需要和类名保持⼀致。
例如:
void Player::Player(std::string name, double hps, double atks)
{
Name =name;
Hp_s = hps;
Atk_s = atks;
hp_();
atk_();
}
这⾥将(⼀⼀五)的⼀个源代码⽂件中,Player类的start函数名改为了Player(和类名保持⼀致),这样的话,就可以在声明类对象的时候,同时对其初始化了。。
需要注意的是,传递的参数名(std::string name, double hps, double atks)不能是私有成员的名字。否则调⽤本函数,实际上便是将私有成员的值赋给⾃⼰。
如代码:
#include<iostream>
#include<string>
class man //创建类
{
private:
std::string name; //私有成员两个
int year;
public:
man(const char*na, int a) //构造函数名同类名
{
name = na; //传递参数来赋值
year = a;
}
void show() //显⽰
{
using namespace std;
cout << name << " is " << year << " years old." << endl;
}
};
int main()
{
using namespace std;
man a = { "wd",27 };
a.show();
system("pause");
return 0;
}
输出:
wd is 27 years old.
请按任意键继续. . .
构造函数的调⽤⽅式:
①显式的:如man a = man("aa", 1); //注意,这⾥是⼩括号(圆的)
②隐式的:如man b("bb", 1); //注意,这⾥也是⼩括号(圆的)
③C++11的列表初始化:如
man c = man{ "cc",1 };
man d = { "dd",1 };
man f{ "ff",1 };
以上三个是⼤括号,区别与⼩括号。⼩括号不⽀持第⼆种⽅式
④使⽤new时:如
man *a = new man{ "wd",27 }; //初始化
(*a).show(); //调⽤需要加括号,不能是*a.show()这样
⑤使⽤构造函数时,必须在声明的时候进⾏初始化,否则会提⽰类不存在默认构造函数。例如上⾯代码中,直接写man a; 编译器是会提⽰错误的;
⑥注意:⽆法使⽤对象名来调⽤构造函数(会提⽰使⽤的类型名);
⼀般来说,构造函数是创建对象时使⽤的。
⑦构造函数的参数,可以设置默认参数,就像使⽤带默认参数的函数那样使⽤(遵守相关规定)。如:
man(const char*na = "xxx", int a = 10)
默认构造函数:
所谓默认构造函数,指的是在未提供显式初始值时,⽤来创建对象的构造函数。
例如,⼀个类,只有私有成员和公有成员(数据和函数),但未提供默认构造函数。那么C++将⾃动提供默认构造函数的隐式版本,不做任何⼯作(即可以只声明对象,但是不赋值)。
例如,若上⾯的man类未提供构造函数的话,那么其默认构造函数可能是:
man(){}
即既⽆参数,也⽆函数代码。
如果要使⽤默认构造函数,那么就应该给对象的私有成员进⾏赋值(⾄少,要有函数定义,不能只有函数原型),否则编译器就会提⽰出错(实际上也容易出问题)。
例如,⾄少是这样:
man(const char*na = "xxx", int a = 10) {}//使⽤默认构造函数的最简形式,注意,这⾥的参数并没有意义
和man() {}这种隐式的默认构造函数,是等价的;
推荐是这样:
man(const char*na = "xxx", int a = 10) //构造函数名同类名
{
name =na; //传递参数来赋值
year = a;
}
析构函数的定义或者这样(效果相同,但前者可以多⼀个选择,以便在初始化时赋值):
man() //默认构造函数
{
name ="xxx"; //提供默认值
year = 1;
}
这样的话,假如⽤户在初始化的时候,若不赋值,则⾃动使⽤默认值。
析构函数:
⽤构造函数(⽆论是默认的还是⽤户⾃⼰定义的)创建对象后,程序负责跟踪该对象,直到过期为⽌。
对象过期时,程序将⾃动调⽤⼀个特殊的成员函数——析构函数。
析构函数负责完成清理⼯作,按照教程所说,其很有⽤。
例如,如果构造函数使⽤new来分配内存,那么析构函数将使⽤delete来释放这些内存。(但貌似如果没有使⽤new,那么析构函数就将⽆事可做)
如果析构函数⽆事可做,那么就让编译器⽣成⼀个什么都不做的隐式析构函数。
构造名称的函数名,和类名是⼀样的;
⽽析构函数的名字,和类名也⼀样,不过前⾯还要额外加“~”。
例如:~man() {}这样
析构函数的原型:
析构函数没有参数,所以其函数原型必然是: ~类名(); 这样
因为析构函数在上⾯那段代码⾥,并没有做什么事,所以使⽤的是隐式析构函数。但如果要展现,也可以为析构函数编写代码。如:
~man() { std::cout <<"end"<< std::endl; }
为了⽅便查看,我们创建⼀个块内的类对象,然后观察其在块结束时析构函数的作⽤,如代码:
#include<iostream>
#include<string>
class man //创建类
{
private:
std::string name; //私有成员两个
int year;
public:
man() //默认构造函数
{
name = "xxx"; //提供默认值
year = 1;
}
void ab() { name = "aaa";year = 5; }
void show() //显⽰
{
using namespace std;
cout << name << " is " << year << " years old." << endl;
}
~man() { std::cout << "end" << std::endl; }
};
int main()
{
using namespace std;
{ //⽤括号括起来的块,类对象只在块内存在
man a;
a.show();
cout << "这⾥还在块内" << endl;
}
cout << "这⾥在块外了" << endl;
system("pause");
return 0;
}
输出:
xxx is 1 years old.
这⾥还在块内
end
这⾥在块外了
请按任意键继续. . .
在离开块后,析构函数执⾏了,因此多了⼀⾏end。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论