c++基础(五)

c++基础(五),第1张

c++编程过程中,我们在创建以及使用对象时往往会对一个对象进行初始化以及在使用结束后对数据进行一个清理的 *** 作。


如果对象没有进行初始状态,则在后期的使用过程中其结果往往也是未知的。


而如果我们在使用完成一个对象或者变量时,没有及时的进行清理,也会造成一点定的安全问题。


因此,在c++中利用了构造函数和析构函数,这两个函数在编译过程中会被自动调用,但是如果我们不提供这两个函数,编译器会提供一个空实现的构造函数和析构函数


一、构造函数

构造函数主要是在创建对象时为对象成员的属性进行赋值,构造函数是由编译器自动调用的,无需进行手动调用。



标准语法:

类名(){}

从语法中可以看出:
1、构造函数没有返回值,不需要写void;
2、函数名称与类名相同;
3、构造函数可以有参数,因此可以发生重载;
4、程序在调用对象时会自动调用构造函数,无须手动调用,而且只会调用一次;

(1)、构造函数的分类以及调用:

构造函数有两种分类方式,分别为按参数分类、按类型分类
(1)、按参数分类:
1、有参构造
2、无参构造(默认构造)
两种类别的分类方法如下:

class cat
{
public:
	//无参构造函数的例子,可以看出来,这个构造函数没有传入任何函数,因此为无参构造函数
	cat()
	{
		cout << "这是一个无参构造函数" << endl;
	}
	//有参构造函数的例子,可以看出,这个构造函数有一个参数传入,所以称为有参构造函数
	cat(int a)
	{
		age = a;
		cout << "这是一个有参构造函数" << endl;
	}
	//这是一个拷贝构造函数,它传入的是一个相同类别的属性,并将相关的属性赋给这一新的类别。


但是在复制的时候不能对原对象的的属性值进行修改,所以需要加一个const cat(const cat &b) { age = b.age; cout << "这是一个有参构造函数" << endl; } private: int age; };

(2)、按类型分类:
1、普通构造
2、拷贝构造
这是一个无参构造函数
这是一个拷贝构造函数 age = -858993460
这是一个析构函数
这是一个析构函数
通常用到拷贝构造函数的时机:
1、当我们使用一个已经创建完毕的对象来初始化一个新的对象时

class cat
{
public:
	//无参构造函数的例子,可以看出来,这个构造函数没有传入任何函数,因此为无参构造函数
	cat()
	{
		cout << "这是一个无参构造函数" << endl;
	}
	//有参构造函数的例子,可以看出,这个构造函数有一个参数传入,所以称为有参构造函数
	cat(int a)
	{
		age = a;
		cout << "这是一个有参构造函数   age = " << age << endl;
	}
	cat(const cat &b)
	{
		age = b.age;
		cout << "这是一个拷贝构造函数   age = " << age << endl;
	}
	~cat(){
		cout << "这是一个析构函数" << endl;
	}
private:
	int age;
};


void test() 
{	
//	cat c1; //默认构造函数调用  调用默认函数时,不要加()
	cat c2(10); //有参构造函数
	cat c3(c2); //拷贝构造函数

2、当我们以值传递的方式给函数参数传值时

class cat
{
public:
	//无参构造函数的例子,可以看出来,这个构造函数没有传入任何函数,因此为无参构造函数
	cat()
	{
		cout << "这是一个无参构造函数" << endl;
	}
	//有参构造函数的例子,可以看出,这个构造函数有一个参数传入,所以称为有参构造函数
	cat(int a)
	{
		age = a;
		cout << "这是一个有参构造函数   age = " << age << endl;
	}
	cat(const cat &b)
	{
		age = b.age;
		cout << "这是一个拷贝构造函数   age = " << age << endl;
	}
	~cat(){
		cout << "这是一个析构函数" << endl;
	}
private:
	int age;
};
void test01(cat c) 
{
	cout << "这是一个测试文件" << endl;
}

void test() 
{	
	cat c1; 
	test01(c1);

此时会输出:

这是一个无参构造函数
这是一个拷贝构造函数   age = -858993460
这是一个测试文件
这是一个析构函数
这是一个析构函数

在这里解释一下上面的输出:当程序运行到test函数中的第一行时,会调用一个无参构造函数,而当运行到第二行时,会将无参构造函数中的值复制一份,传递给test01函数,所以会调用拷贝构造函数来拷贝一份数值,因为默认构造函数没有age这一变量,所以会出现乱码情况
3、当我们以值方式返回一个局部对象时

cat test02()
{
	cat c1;
	cout << (int*) &c1 << endl;
	return c1;

}
void test() 
{	
	cat c = test02();
	cout << (int*)&c << endl;

当调用test时,此时会输出:

这是一个无参构造函数
006FF99C
这是一个拷贝构造函数   age = -858993460
这是一个析构函数
006FFA94
这是一个析构函数

当调用test02时,test02中的第一行代码会创建一个无参构造函数c1,而当运行第二行代码时,编译器会复制一份c1来返回,所以会调用一个拷贝狗构造函数来返回一个局部对象。


从打印出来的地址我们可以,两个对象的地址是不一样的,这也证明是调用了拷贝构造函数。


构造函数有三种种调用形式,分别为括号法、显示法、隐式转换法
(1)、括号法
样例如下:

class cat
{
public:
	//无参构造函数的例子,可以看出来,这个构造函数没有传入任何函数,因此为无参构造函数
	cat()
	{
		cout << "这是一个无参构造函数" << endl;
	}
	//有参构造函数的例子,可以看出,这个构造函数有一个参数传入,所以称为有参构造函数
class cat
{
public:
	//无参构造函数的例子,可以看出来,这个构造函数没有传入任何函数,因此为无参构造函数
	cat()
	{
		cout << "这是一个无参构造函数" << endl;
	}
	//有参构造函数的例子,可以看出,这个构造函数有一个参数传入,所以称为有参构造函数
	cat(int a)
	{
		age = a;
		cout << "这是一个有参构造函数   age = " << age << endl;
	}
	cat(const cat &b)
	{
		age = b.age;
		cout << "这是一个拷贝构造函数   age = " << age << endl;
	}
	~cat(){
		cout << "这是一个析构函数" << endl;
	}
private:
	int age;
};


void test() 
{	
	cat c1; //默认构造函数调用  调用默认函数时,不要加()
	cat c2(10); //有参构造函数
	cat c3(c2); //拷贝构造函数

}

其输出结果为:

这是一个无参构造函数
这是一个有参构造函数   age = 10
这是一个拷贝构造函数   age = 10
这是一个析构函数
这是一个析构函数
这是一个析构函数

这里解释一下为什么在调用默认构造函数时不要加()
我们在一般情况下,可以通过void test()来调用test函数的声明。


在调用默认函数时加括号的话,其语法变为cat c1()。


这会让编译器认为我们在声明一个c1的函数,其返回值为cat类。


(2)、显示法
显示法是将一个匿名对象进行一个命名 *** 作
样例代码如下:

	cat c1; //默认构造函数调用
	cat c2 = cat(10); //有参构造函数
	cat c3 = cat(c2); //拷贝构造函数   	

在用显示法来调用构造函数时有两点注意事项:
1、cat(10)为一个匿名对象,当本行代码执行结束后,因为其没有名称,所以无法在后续代码中进行调用,所以系统会立即回收这一匿名对象。



2、不可以利用拷贝构造函数来初始化一个匿名对象例如:cat(c2)。


如果这样定义的话会出现cat c2重定义。


因为当我们代码为cat(c2)时,此时的编译器会认为cat(c2)等价于cat c2。


因为之前的c2已经定义过。


所以会出现重定义报错。



(3)、隐式转换法
样例代码如下:

	cat c1 = 10; //有参构造函数
	cat c2 = c1; //拷贝构造函数

之所以称为隐式转换法是因为当我们的代码为上面样例的时候,编译器会隐式的将以上的代码转换为:cat c1 = cat(10)


二、析构函数

主要作用在对象销毁前,由系统自动调用,无须手动调用,而且只会调用一次。



标准语法:

~类名(){}

从语法中可以看出:
1、构造函数没有返回值,不需要写void;
2、函数名称与类名相同,需要在名称前面加上符号~;
3、析构函数不可以有参数,因此不可以发生重载,
4、在对象销毁前系统会自动调用,来执行一些清理的工作,无须进行。



以上就是构造函数和析构函数的具体分享
那么在c++中,构造函数是怎么进行调用的呢?


三、构造函数的调用规则

在默认情况下,c++编译器至少给一个类添加三个函数
1、默认构造函数(无参,函数体为空)
2、默认析构函数(无参,函数体为空)
3、默认拷贝函数
那么当我们有自己定义的有参构造函数了,则c++不会再提供默认的无参构造函数了,但是会提供一个默认的拷贝构造函数,那么如果我们自己定义了拷贝构造函数,那么c++将不会提供其他的构造函数了。



样例如下:

class cat
{
public:
	cat(int age)
	{
		Age = age;
		cout << "这是一个有参构造函数" << endl;
	}

	cat(const cat &c1)
	{
		Age = c1.Age;
		cout << "这是一个拷贝构造函数" << endl;
	}

	~cat()
	{
		cout << "这是一个析构函数" << endl;
	}

private:
	int Age;
};

void test()
{
	cat c;
}

运行以上代码会报错:类 “cat” 不存在默认构造函数
因为我们在之前中已经说明:当我们有自己定义的有参构造函数了,则c++不会再提供默认的无参构造函数。


所以当我们在调用无参构造函数时会报错。



另一个样例如下:

class cat
{
public:
	cat(const cat &c1)
	{
		Age = c1.Age;
		cout << "这是一个拷贝构造函数" << endl;
	}

	~cat()
	{
	
		cout << "这是一个析构函数" << endl;
	}

private:
	int Age;
};

void test()
{
	cat c(10);
}

以上代码会报错:“cat::cat(const cat &)”: 无法将参数 1 从“int”转换为“const cat &”
因为我们在之前中已经说明:当我们有自己定义的拷贝构造函数了,则c++不会再提供其他的构造函数。


所以当我们在调用有参构造函数时会报错。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/564125.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-03
下一篇 2022-04-03

发表评论

登录后才能评论

评论列表(0条)

保存