构造函数和析构函数
-
构造函数
-
默认构造函数
-
default关键字
-
explicit关键字
-
使用成员初始化列表
-
委托构造
-
副本构造函数
-
析构函数
构造函数
-
问题背景:类的初始化
//没有私有成员变量的类可以初始化
class T
{
public:
int hp;
int mp;
}
int main()
{
T t1{100,200};
}
-
getter和setter
class T
{
private:
int mp;
public:
int hp;
int GetMp() const {return mp;}//getter
void SetMp(int _mp) {mp=_mp;}//setter
}
int main()
{
T t2;
t2.hp=1997;
t2.SetMp(1999);
T t1{t2};//或t1=t2, 可以将t2中成员变量的值都赋给t1
std::cout<<t1.hp<<" "<<t1.GetMp();//1997 1999
}
构造函数:
在类的定义中有一种特殊的成员函数叫做构造函数 , 构造函数在类被创建时自动被调用 ; 一般用来创建新的类实例时执行初始化操作。
构造函数与它所在的类同名 , 并且没有返回值 , 任何类都至少有一个构造函数;
class ROLE
{
private:
int hpRecover;
void Init()
{
hpRecover=3;
}
public:
ROLE()
{
Init();
}
int hp;
int damage;
void Act(ROLE& role)
{
role.hp-=damage;
}
}
多个构造函数
#include <iostream>
#include "Role.h"
class T
{
private:
int mp;
public:
int hp;
int GetMp() const { return mp; }//getter
void SetMp(int _mp) { mp = _mp; }//setter
T()
{
hp = 300;
mp = 400;
}
T(int _hp,int _mp) //相当于函数重载
{
hp = _hp;
mp = _mp;
}
T(T& t) //传引用
{
hp = t.hp;
mp = t.GetMp();
}
};
//如果不传引用
/*
T(T t)
{
hp = t.hp;
mp = t.GetMp();
}
T t3(t2);//t2也会调用一次构造函数,以此类推造成递归
*/
int main()
{
T t1;
std::cout << t1.hp << " " << t1.GetMp()<<std::endl;//300 400
T t2(100, 200);
std::cout << t2.hp << " " << t2.GetMp() << std::endl;//100,200
T t3(t2);
std::cout << t3.hp << " " << t3.GetMp() << std::endl;//100,200
}
默认构造函数
如果一个类没有显示的定义一个构造函数, 那么编译器会自动添加一个默认的构造函数, 这个默认构造函数是无参数,无返回值的函数
class ROLE
{
private:
int hpRecover;
public:
ROLE(){};
int hp;
int damage;
void Act(ROLE& role)
{role.hp-=damage;}
}
若已经写了构造函数,编译器就不会自动添加默认构造函数,要自己手动写
default关键字
只要定义过构造函数,默认构造函数就不会被定义,有时候我们希望还可以被默认构造,就需要类有一个默认构造函数
自己定义默认构造函数
Role(){}
通过default来定义默认构造函数
Role()=default;
如果在默认构造函数中什么都不做,这两种方式都是可以的,但是更推荐使用default关键字,因为效率更高
explicit关键字
被explicit关键字修饰的构造函数会禁用类型转换
class ROLE
{
private:
int hpRecover;
public:
ROLE() {};
int hp;
int damage;
ROLE (int _hp)
{
hp = _hp;
}
bool bigger(ROLE r)
{
return hp > r.hp;
}
};
int main()
{
ROLE r1(100);
std::cout << r1.bigger(500);//0
//利用500,调用ROLE(int _hp)初始化了一个r,然后用这个临时的r与r1比较
//相当于r1.bigger((ROLE(500)); 即进行了类型转换
}
//利用explicit可以解决这个问题
class ROLE
{
private:
int hpRecover;
public:
ROLE() {};
int hp;
int damage;
explicit ROLE (int _hp)
{
hp = _hp;
}
bool bigger(ROLE r)
{
return hp > r.hp;
}
};
int main()
{
ROLE r1(100);
std::cout << r1.bigger(500);//报错
}
使用成员初始化列表
class ROLE
{
private:
int lv;
int hpRecover;
public:
ROLE(int _lv,int _damage):lv{_lv},damage{_damage}
{}
int hp;
int damage;
void Act(ROLE& role);
};
使用成员初始化列表的优势:
ROLE(int _lv,int _damage):lv{_lv}, damage{_damage}
{}
-
效率更高
-
在某些情况下,只能使用这种方式进行初始化(在继承时可以且只能用这种方式初始化基类)
缺点:分配完内存空间后,从上往下赋值
使用成员初始化列表的注意事项
使用成员初始化列表这样的方式构造类,要注意一个问题,即为成员赋值的顺序不是依据代码的顺序,而是成员变量在类的出现顺序;
class ROLE
{
private:
int hp;
int hpRecover;
int lv;
public:
ROLE(int _lv,int _damage);lv{_lv}.damage{_damage},hp{lv*100},hpRecover{_lv)
{}
int damage;
void Act(ROLE& role);
};
//实际初始化顺序
//hp=lv*100; 所以hp结果是随机值
//hpRecover=lv;lv=_lv;
//damage=_damage;
委托构造
class ROLE
{
private:
int hp;
int hpRecover;
int lv;
public:
ROLE():ROLE(100,200)//委托下面的ROLE构造
{}
ROLE(int lv,int ..damage):lv{Llv}.damage{_damage}
{}
int damage;
void Act(ROLE& role);
};
委托构造函数初始化列表里不能初始化成员变量,且一个委托构造函数只能调用一次同一个类的构造函数
副本构造函数
Role role1;
Role role2(role1);
编译器为类指定了一个默认的副本构造函数(会把所有值都初始化),我们也可以手动指定副本构造函数
class ROLE
{
private:
int hp{ 100 };
public:
int lv{ 100 };
int GetHp() { return hp; }
ROLE() {};
ROLE(int _lv,int _hp) :lv{ _lv }, hp{_hp}
{}
//必须传引用,否则死递归
ROLE(const ROLE& role) :hp{ role.hp }//只会复制hp的值
{
std::cout << "调用了副本构造函数" << std::endl;
}
};
int main()
{
ROLE r1(300,500);
ROLE r2(r1);//只复制了r1的hp的值
//"调用了副本构造函数"
std::cout << r1.lv << " " << r1.GetHp() << std::endl;//300 500
std::cout << r2.lv << " " << r2.GetHp() << std::endl;//100 500
}
Role user(300,500);
Role userA=user;//因为userA还没有分配内存空间,所以会调用副本构造函数
userA=user;//不调用副本构造函数了
析构函数
创建类——构造函数
销毁类——析构函数
在类中还有一种特殊的成员函数,叫做析构函数,析构函数在类的生命周结束时,被自动调用, (用来销毁一个类), 一般用来做扫尾工作,比如释放内存,关闭句柄等等,
如果一个类没有定义析构函数,那么编译器会自动添加一个空的析构函数,析构函数只能有一个;
手动定义析构函数的语法
~类名()
{}
析构函数没有参数, 没有返回类型, 一般不会手动调用, 如果类的析构函数为空, 最好使用关键字default来定义, 例如:
~ROLE()=default;
class Role
{
int* ary;
Role()
{
ary=new int[100];
std::cout<<"类被创建"<<std::endl;
}
~Role()
{
delete[] ary;
std::cout<<"类被销毁"<<std::endl;
}
};
编译器自动定义的内容:
● 默认构造函数
● 默认副本构造函数
● 析构函数