前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++干货基地】六大默认成员函数: This指针 | 构造函数 | 析构函数

【C++干货基地】六大默认成员函数: This指针 | 构造函数 | 析构函数

作者头像
鸽芷咕
发布2024-05-26 16:40:53
550
发布2024-05-26 16:40:53
举报
文章被收录于专栏:C++干货基地C++干货基地

一、this指针的概念

1.1 this 指针的引入

来讲 this 之前我们先来看一下下面这段代码:

  • 这里 printf 函数我们并没有传递参数那么他如何知道我们打印的是那个对象对的呢?
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

class Data
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Data d1,d2;
	d1.Init(2023, 2, 27);
	d2.Init(2023, 4, 07);
	d1.print();
	return 0;
}

这里我们去调用 d1.print(); 但是 print 函数并没有参数是如何知道要打印 ?

其实在C++这里虽然我们没有给print 函数参数,但实际上是有一个默认的this 指针来自动调用的,实际的代码可能是下面这样但是编译器给自动化了大大简化了用户操作。

C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

1.2 this 指针的特性

  1. this指针的类型:类类型 const,即成员函数中,不能给this指针赋值。*
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
this 指针存在哪里

this 前面我们说了 其实是我们成员函数的形参,所以 this 指针其实是存放在 栈区的。(有些编译比如vs可能会用寄存器存储)

this指针可以为空吗?

这个问题我们就来看下下面这俩个练习题了:

🍸 代码演示:

代码语言:javascript
复制
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

这个小程序相信大家都看得出来是选 C 虽然我们的 p 是一个空指针但是调用函数的时候并不会传递this 指针,或者使用空指针所以程序正常运行

🍸 代码演示:

代码语言:javascript
复制
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}

而这个程序就不可以了,我们对空指针进行解引用一定对引发程序崩溃

  • 因为我们使用了成员变量 _a ,但成员变量的地址又是一个空地址

二、六个默认成员函数

如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员 函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

同时这六个默认成员函数也是我们面向对象的核心,下面我们就来先介绍一下构造函数和析构函数

三、构造函数

3.1 构造函数的概念

构造函数咋一听名字各位是不是觉得他是用来创建函数的,其实大家是被名字误导了。构造函数的实际作用就是 和 Init 函数一样,用来初始化我们的对象:

  • 以往我们初始化对象都需要自己去手动调用,非常麻烦
  • 单是构造函数这种默认成员函数就不需要了,自动调用帮我们初始化化
代码语言:javascript
复制
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Init(2022, 7, 5);
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);
	d2.Print();
	return 0;
}

这里我们每次调用日期类都要给它,去初始化太麻烦了,但是使用构造函数来进行初始化就方便很多

🍸 代码演示:

代码语言:javascript
复制
class Date
{
public:
	Date(int year = 2023, int month = 2, int day = 29)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Print();

	Date d2(2022, 7, 6);
	d2.Print();
	return 0;
}

3.2 构造函数的特性

构造函数是一个默认成员函数,也是一个非常特殊的成员函数。可以帮助我们自动初始化对象,而且自动调用它有以下一个特性

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
无参构造函数

有些场景下我们肯需要无参构造函数,但是很多新手在调用的时候总会出现调用错误:

  • 无参构造函数在调用的时候,是不需要写括号的
  • 对象后面不用跟括号,否则就成了函数声明
代码语言:javascript
复制
class Date
{
public:
    // 1.无参构造函数
    Date()
    {}

    // 2.带参构造函数
    Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void TestDate()
{
    Date d1; // 调用无参构造函数
    Date d2(2015, 1, 1); // 调用带参的构造函数

    // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
    // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
    // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
    Date d3();
}
无参的默认构造函数

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。

但是这个构造函数对,内置类型不处理,对自定义类型调用它的默认函数

比如说这里我们就没有去显示创建构造函数,但是自动创建了一个默认构造函数,默认构造函数

  • 对自定义类型调用他的构造函数
  • 对内置类型不做处理

这里很多人就觉得为什么对内置类型不处理,自动处理了不是更好吗?可能是祖师爷在编写的打了个盹给搞忘了(哈哈哈开个玩笑)

  • 🔥 所以在C++11的时候对这里打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

🍸 代码演示:

代码语言:javascript
复制
class Date
{
public:
	void print()
	{
		cout << _year  << _month  << _day << endl;
	}

private:
	int _year = 2023;
	int _month = 2;
	int _day = 17;
};

int main()
{
	Date a1;
	return 0;
}

📑代码结果:

  • 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数

四、析构函数

4.1 析构函数函数的概念

构造函数是用来自动初始化对象的,那么自动清理对象是用那个呢?没错这就是析构函数该干的工作了,析构函数主要负责清理空间和我们以前数据结构中的 destroy 销毁空间的作用是一样的!

4.2 析构函数的特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

🍸 代码演示:

代码语言:javascript
复制
class Date
{
public:
	void print()
	{
		cout << _year  << _month  << _day << endl;
	}
	~Date()
	{
		cout << "time~" << endl;
	}
private:
	int _year = 2023;
	int _month = 2;
	int _day = 23;
};

int main()
{
	Date a1;
	return 0;
}

这里我们就可以看到了,我们不取调用析构函数,但是在程序结束的时候自动调用

  • 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器 生成的默认析构函数,对自定类型成员调用它的析构函数。

这里析构函数和构造函数是一模一样的,对自定义类型才会去默认调用它的析构函数

代码语言:javascript
复制
class Time
{
public:
 ~Time()
 {
 cout << "~Time()" << endl;
 }
private:
 int _hour;
 int _minute;
 int _second;
};
class Date
{
private:
 // 基本类型(内置类型)
 int _year = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d;
 return 0;
}
  • 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

因为析构函数一般是用来释放我们申请的空间,而内置类型在程序结束会自动释放空间所以没有申请资源可以不写析构函数

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、this指针的概念
    • 1.1 this 指针的引入
      • 1.2 this 指针的特性
        • this 指针存在哪里
        • this指针可以为空吗?
    • 二、六个默认成员函数
    • 三、构造函数
      • 3.1 构造函数的概念
        • 3.2 构造函数的特性
          • 无参构造函数
          • 无参的默认构造函数
      • 四、析构函数
        • 4.1 析构函数函数的概念
          • 4.2 析构函数的特性
          相关产品与服务
          云开发 CloudBase
          云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档