【C++】类的构造函数和析构函数

函数后面跟着圆括号表示参数初始化,比如int i(1)等价于int i=1。

目录

构造函数

默认构造函数

析构函数

全局变量的生命周期

.h和.cpp分开写(参考博客)

编译器默认添加构造和析构函数

成员的初始化与析构

初始化列表


构造函数

了解完【C++】类之后,再来看看类的构造函数。

类的实例就是对象,一个对象如果没有初始值,那不能算对象。所以这时就需要一个函数能在类被实例化时,直接就行初始化,这就是构造函数。构造函数在创建对象时,会被自动调用。

构造函数是类的一种特殊的成员函数:

(1)函数名与类名必须相同;

(2)没有返回值;

(3)构造函数可以被重载(参数列表不同就行)。

#include "pch.h"
#include <iostream>
#include "stdio.h"

class Circle
{
public:
	int x, y;
	int radius;

	Circle()
	{
		printf("This is the structure of no param.\n");
		x = 0;
		y = 0;
		radius = 1;
	}

	Circle(int x, int y, int radius)
	{
		printf("This is the structure with params.\n");
		this->radius = radius;
		this->x = x;
		this->y = y;
	}
};

int main()
{
    Circle a;
    //Circle a();这是错误的,根本没有这种使用方式,它不会调用任何构造函数。
    Circle b(1,1,1);
    std::cout << "Hello World!\n"; 
    return 0;
}

注意:上面main函数中有一段被我注释,Circle a();这种方式编译会通过,但是没有调用任何构造函数,我不知道为什么可以这样,我很蒙蔽。

默认构造函数

有2中情况属于默认构造函数:

(1)构造函数,没有参数;

(2)构造函数,所有参数都有确定值。

为什么需要默认构造函数?

如果没有默认构造函数,你申明对象数组时就会出错。比如Circle a[4];这种形式就直接报错。

析构函数

如果一个变量和函数跳出了它自身的作用域,那么它就会调用析构函数,然自己所占有的内存被释放。

作用域怎么看?

找这个变量或者函数最近的大括号,跳出这个大括号就会失效,就会自动调用析构函数。

#include "pch.h"
#include <iostream>
#include "stdio.h"

class Circle
{
public:
    int x, y;
    int radius;

    Circle()
    {
        printf("This is the structure of no param.\n");
        x = 0;
        y = 0;
        radius = 1;
    }

    ~Circle()
    {
        printf("This is xigou function.\n");
    }
};

int main()
{
	//对象a跳出这个大括号,就跳出了它的作用域,那么就会自动调用析构函数
    {
        Circle a;
    } 
    std::cout << "Hello World!\n"; 
    return 0;
}

全局变量的生命周期

全局变量是在主函数中所有程序都结束完成后,最后才会销毁全局变量。具体例子如下:

#include "pch.h"
#include <iostream>
#include "stdio.h"

class Circle
{
public:
    ~Circle()
    {
        printf("This is xigou function.\n");
    }
};

Circle a;

int main()
{
    std::cout << "Hello World!\n"; 
    return 0;
}

/*
//run out
Hello World!
This is xigou function.
*/

.h和.cpp分开写(参考博客

.h文件

class Circle
{
public:
    Circle(); //函数申明
    ~Circle();
};

.cpp文件

#include "Circle.h"
Circle::Circle(){} //函数实现
Circle::~Circle(){}

再来个学生的例子:

.h文件

#pragma once
#ifndef _DATASTORE_H
#define _DATASTORE_H
struct Student
{
	int id;
	char name[32];
	Student* next;
};

class DataStore
{
public:
	DataStore();
	~DataStore();
private:
	Student m_head;
};

#endif

.cpp文件

#include "pch.h"
#include "DataStore.h"
#include "stdlib.h"

DataStore::DataStore()
{
	m_head = {0};

}

DataStore::~DataStore()
{
	Student* p = m_head.next;
	while(p)
	{
		Student* next = p->next;
		free(p);
		p = next;
	}
}

编译器默认添加构造和析构函数

如果你没有自己写构造和析构函数,他会自动添加一个简单的构造和析构函数。

Circle::Circle(){}
Circle::~Circle(){}

成员的初始化与析构

考虑成员变量本身就是class类型的情况:

(1)当对象被构造时,成员变量也被构造(成员变量的构造函数被调用);

(2)当对象被析构时,成员变量也被析构(成员变量的析构函数被调用)。

#include "pch.h"
#include <iostream>
#include "stdio.h"

class Other
{
public:
	Other()
	{
		printf("Other: 创建\n");
	}
	~Other()
	{
		printf("Other: 销毁\n");
	}
};

class Child
{
public:
	Child()
	{
		printf("Child: 创建\n");
	}
	~Child()
	{
		printf("Child: 销毁\n");
	}
};

class Object
{
public:
	Object()
	{
		printf("Object: 创建\n");
	}
	~Object()
	{
		printf("Object: 销毁\n");
	}
private:
	Child m_child;
	Other m_other;
};

int main()
{
	{
		Object obj;
	}
	std::cout << "Hello World!\n"; 
	return 0;
}

小结从结果我们可以得出,你要想创建object对象,必须在object内的所有内容编译成功后才会有一个完整的object。所以需先完成自身的构造函数,然后按照顺序对child和other进行对象的创建,这一系列操作结束后,就完成了对object对象的构建。

在销毁的时候为什么是object先销毁呢?你想如果不告诉计算机你要销毁object了,那么other和child凭什么知道自己被销毁了。好像理解的有问题。

构造和析构的顺序总是相反的。

初始化列表

可以在构造函数后面直接初始化

(1)以冒号引导;

(2)使用小括号来初始化。

class Child
{
public:
	Child()
	{
		printf("Child: 创建\n");
	}
	Child(int x, int y)
	{
		printf("Test initial function.");
	}
	~Child()
	{
		printf("Child: 销毁\n");
	}
};

class Object
{
public:
	Object() : m_child(1, 2), x(1), y(2)  //这里就是初始化参数列表
	{
		printf("Object: 创建\n");
	}
	~Object()
	{
		printf("Object: 销毁\n");
	}
private:
	Child m_child;
	int x, y;
};


int main()
{
	Object obj;
	std::cout << "Hello World!\n"; 
	return 0;
}

初始化和赋值有什么异同?

(1)功能上,他们一样;

(2)性能上,初始化相对较好,有时可以节省CPU操作。(暂时不是很明白)

下面这个例子是有问题的,编译器直接提示你Child没有可以用的构造函数。

比如下面的例子,在Object类中创建了Child的对象m_child,但没有给x,y的初始化参数,导致函数不能构造,直接编译报错。因为一个类必须要有构造函数,你不写,编译器会自动帮你加,但你写了,参数没给对,不能进行构造,那就出问题了。

class Child
{
public:
	Child(int x, int y)
	{
		printf("Test initial function.");
	}
	~Child()
	{
		printf("Child: 销毁\n");
	}
public:
	int x, y;
};

class Object
{
public:
	Object()
	{
		printf("Object: 创建\n");
	}
//改为下面这种形式就可以了
        //Object() : m_child(1,2)
	//{
	//	printf("Object: 创建\n");
	//}
	~Object()
	{
		printf("Object: 销毁\n");
	}
private:
	Child m_child;
	int x, y;
};


int main()
{
	Object obj;
	std::cout << "Hello World!\n"; 
	return 0;
}

 

有一个比较关键的点:在类中初始化和在主函数中初始化是不一样的,而且他们都只能用这种初始化方式,其他初始化方式都会报错。当然如果你使用指针就可以用new的方式来初始化,比如Child* a = new Child(1,2),这种指针的方式,不管是在类中还是在主函数中都是可以使用的。

在类中初始化:

class Object
{
public:
	Object() : m_child(1,2)
	{
		printf("Object: 创建\n");
	}
	~Object()
	{
		printf("Object: 销毁\n");
	}
private:
	Child m_child;
	Other m_other;
	int x, y;
};

在主函数中初始化:

int main()
{
	Child m_child(1,2);
	std::cout << "Hello World!\n"; 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值