构造函数和析构函数

文章详细介绍了C++中类的构造函数和析构函数的概念及用法,包括默认构造函数、显式构造函数、成员初始化列表、委托构造和副本构造函数。同时讨论了explicit关键字的作用以及析构函数在对象生命周期结束时的角色。
摘要由CSDN通过智能技术生成

构造函数和析构函数

  • 构造函数

  • 默认构造函数

  • 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}

{}

  1. 效率更高

  1. 在某些情况下,只能使用这种方式进行初始化(在继承时可以且只能用这种方式初始化基类)

缺点:分配完内存空间后,从上往下赋值

使用成员初始化列表的注意事项

使用成员初始化列表这样的方式构造类,要注意一个问题,即为成员赋值的顺序不是依据代码的顺序,而是成员变量在类的出现顺序;


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;
	}
};

编译器自动定义的内容:

● 默认构造函数

● 默认副本构造函数

● 析构函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值