c++之构造函数与析构函数

目录

构造函数的概念

析构函数的概念

有参构造函数的使用

 拷贝构造函数的使用

特殊情况


构造函数的概念

1构造函数定义及调用

1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;

2)构造函数在定义时可以有参数;

3)没有任何返回类型的声明。

2构造函数的调用

自动调用:一般情况下C++编译器会自动调用构造函数

手动调用:在一些情况下则需要手工调用构造函数

析构函数的概念

3)析构函数定义及调用

1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数

语法:~ClassName()

2)析构函数没有参数也没有任何返回类型的声明

3)析构函数在对象销毁时自动被调用

4)析构函数调用机制

C++编译器自动调用


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Test
{
public:
	Test() //无参数构造函数
	{
		p = (char *)malloc(100);
		strcpy(p, "aaaa");
		a = 10;
		cout << "我是构造函数 被执行了" << endl;
	}

	void print()
	{
		cout << p << endl;
		cout << a << endl;
	}
	~Test()
	{
		if (p)
		{
			free(p);
			p = NULL;
		}
		cout << "我是析构函数 被执行了" << endl;
	}
private:
	int a;
	char *p;
};

//给对象搭建一个舞台,研究对象的行为
void objplay()
{
	Test t1;
	t1.print();
}

int main()
{
	objplay();
	cout << "hello" << endl;
	system("pause");
	return 0;
}

我们可以看到当定义一个变量的时候,c++编译器会自动调用他的构造函数,当变量销毁的时候就自动调用了析构函数。

有参构造函数的使用

构造函数主要分为三类,即:默认构造函数、拷贝构造函数、有参构造函数。

有参构造函数的初始化主要有三种:括号法、=号法、直接调用构造函数,手动的调用构造函数

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Test2
{
public:
	Test2()
	{
		m_a = 0;
		m_b = 0;
		cout << "无参数构造函数" << endl;
	}

	Test2(int a,int b)
	{
		m_a = a;
		m_b = b;
		cout << "有参数构造函数" << endl;
	}

	Test2(int a)
	{
		m_a = a;
		m_b = 0;
		cout << "有参数构造函数" << endl;
	}

	//赋值构造函数(copy构造函数)
	Test2(const Test2 &obj)
	{
		cout << "我也是构造函数" << endl;
	}
	void prinT()
	{
		cout << "普通变量函数" << endl;
	}
private:
protected:
	int m_a;
	int m_b;
};


int main()
{
	//1括号法
	Test2 t1(3, 4);  //c++编译器帮我们自动调用构造函数
	t1.prinT();

	//2=号法
	Test2 t2 = (3, 4); //c++对等号操作符进行功能增强

	//3 直接调用构造函数,手动的调用构造函数
	Test2 t3 = Test2(1, 2); //匿名对象


	cout << "hello" << endl;
	system("pause");
	return 0;
}

 

这边解释一下括号法,由于括号表达式取的是最后一个操作符的值,即Test2 t2= (3,4)等价于

Test2 t2 = 4;所以调用的是一个参数的有参构造函数。

 拷贝构造函数的使用

拷贝构造函数主要有四种使用场景

第一种:定义变量初始化

第二种:括号法

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Test4
{
public:
	Test4()
	{
		m_a = 0;
		m_b = 0;
		cout << "无参数构造函数" << endl;
	}

	Test4(int a, int b)
	{
		m_a = a;
		m_b = b;
		cout << "有参数构造函数" << endl;
	}

	Test4(int a)
	{
		m_a = a;
		m_b = 0;
		cout << "有参数构造函数" << endl;
	}

	//赋值构造函数(copy构造函数)
	Test4(const Test4 &obj)
	{
		cout << "我也是构造函数" << endl;
		m_a = obj.m_a + 100;
		m_b = obj.m_b + 100;
	}
	void prinT()
	{
		cout << "普通变量函数" << endl;
		cout << "m_a" << m_a << endl;
		cout << "m_b" << m_b << endl;
	}
private:
	int m_a;
	int m_b;
protected:

};

//1 赋值构造函数 用1个对象去初始化另外一个对象
int main()
{
	Test4 t0(1, 2);
	Test4 t1(1, 2);
	t0 = t1;  //用t1给t0赋值,和初始化是两个不同的概念

	//第一种调用方法
	Test4 t2 = t1; //用t1来初始化t2
	t2.prinT();
	//第二种调用方法
	Test4 t3(t1);
	t3.prinT();

	cout << "hello" << endl;
	system("pause");
	return 0;
}

第一种调用方法是利用其他变量来初始化,这种方法需要注意的是需要在定义变量的时候就初始化该变量,如果在定义后在进行赋值操作的话就不是调用拷贝构造函数,调用的就是重载=操作符的方法。

第二种方法利用的是括号法,即在初始化的时候给定参数进行调用。

第三种方法是利用实参初始化形参的调用拷贝构造函数。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Location
{
public:
	Location(int xx = 0, int yy = 0)
	{
		X = xx;  Y = yy;  cout << "Constructor Object.\n";
	}

	//copy构造函数  完成对象的初始化
	Location(const Location & obj) //copy构造函数 
	{
		X = obj.X; Y = obj.Y;
	}
	~Location()
	{
		cout << X << "," << Y << " Object destroyed." << endl;
	}
	int  GetX() { return X; }		int GetY() { return Y; }
private:   int  X, Y;
};

//业务函数
void f(Location p)
{
	cout << p.GetX() << endl;
}

void playobj()
{
	Location a(1, 2);
	Location b = a;
	cout << "b对象已经初始化完毕" << endl;

	f(b); //b实参去初始化形参p,会调用copy构造函数
}

int main()
{
	playobj();
	cout << "hello" << endl;
	system("pause");
	return 0;
}

 我们可以看到在执行f函数的时候会先调用拷贝构造函数初始化形参p ,在调用函数体。

第四种方法是利用函数的返回值来调用拷贝构造函数

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Location
{
public:
	Location(int xx = 0, int yy = 0)
	{
		X = xx;  Y = yy;  cout << "Constructor Object.\n";
	}

	//copy构造函数  完成对象的初始化
	Location(const Location & obj) //copy构造函数 
	{
		X = obj.X; Y = obj.Y;
	}
	~Location()
	{
		cout << X << "," << Y << " Object destroyed." << endl;
	}
	int  GetX() { return X; }		int GetY() { return Y; }
private:   int  X, Y;
};

//业务函数
void f(Location p)
{
	cout << p.GetX() << endl;
}

void playobj51()
{
	Location a(1, 2);
	Location b = a;
	cout << "b对象已经初始化完毕" << endl;

	f(b); //b实参去初始化形参p,会调用copy构造函数
}

int main51()
{
	playobj51();
	cout << "hello" << endl;
	system("pause");
	return 0;
}

我们可以看到g函数在返回值的时候调用了拷贝构造函数返回一个匿名对象,这是由于A变量是存放在栈空间中的,所以他的生存周期只有在g函数里,所以返回他在外部无法使用,明显不合适,所以c++编译器返回的一个拷贝构造函数的匿名对象。

这边在接收匿名对象的时候也有两种情况

一种是:用匿名对象初始化m,此时c++编译器直接把匿名对象转成变量

另一种是:用匿名对象赋值给变量,然后匿名对象析构 

这个是第一种情况 ,此时c++编译器把匿名对象直接转化成m变量,所以只有在函数结束的时候才会调用析构函数。

这是第二种情况, 此时由于调用了赋值操作,所以匿名函数在赋值后直接被析构掉。

特殊情况

二个特殊的构造函数

1)默认无参构造函数

当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2)默认拷贝构造函数

当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数

2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数

3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数

4 )默认拷贝构造函数成员变量简单赋值

总结:只要你写了构造函数,那么你必须用。

1)构造函数是C++中用于初始化对象状态的特殊函数

2)构造函数在对象创建时自动被调用

3)构造函数和普通成员函数都遵循重载规则

4)拷贝构造函数是对象正确初始化的重要保证

5)必要的时候,必须手工编写拷贝构造函数

引用上面的案例 ,如果我们把有参构造函数的默认值去掉的话,在初始化对象的时候没有给参数的话,就会出现没有默认构造函数可以调用的情况,这就是由于当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数,所以出现错误。