C++类和对象中

Posted 蚍蜉撼树谈何易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++类和对象中相关的知识,希望对你有一定的参考价值。

认识类中的6个默认成员函数

构造函数

目的:主要为对象 完成初始化的操作,不负责给对象开辟空间。
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次

编译器会自动调用构造函数

代码:

class student
{
public:
	student(const char* _name, int _age, double _score)
	{
		strcpy(name, _name);
		age = _age;
		score = _score;
	}
private:
	char name[15];
	int age;
	double score;
};
int main()
{
	student m1("张三", 18, 90.0);
	system("pause");
	return 0;
}

在这里插入图片描述

构造函数是可以进行重载的

using namespace std;
class student
{
public:
	student()
	{
		strcpy(name, "李四");
		age = 20;
		score = 91.4;
	}
	student(const char* _name, int _age, double _score)
	{
		strcpy(name, _name);
		age = _age;
		score = _score;
	}
private:
	char name[15];
	int age;
	double score;
};
int main()
{
	student m1("张三", 18, 90.0);
	student m2;
	system("pause");
	return 0;
}

在这里插入图片描述

如果没有显式的给出构造函数,则编译器会自动生成一个函数作为默认构造函数

一般生成的是这个样子

//什么都没有
student()
{
}

这是自测的

class student
{
public:
	student()
	{
		strcpy(name, "李四");
		age = 20;
		score = 91.4;
	}
	student(const char* _name, int _age, double _score)
	{
		strcpy(name, _name);
		age = _age;
		score = _score;
	}
	void print()
	{
		cout << "name" << name << endl;
		cout << "年龄" << age << endl;
		cout << "成绩" << score << endl;
	}
private:
	char name[15];
	int age;
	double score;
};
class person
{
public:
	
private:
	char id[20];
	student m1;

};
int main()
{
	//student m1("张三", 18, 90.0);
	person m2;
	system("pause");
	return 0;
}

在这里插入图片描述

无参构造函数与全缺省构造函数不能同时存在

这里解释一下什么是全缺省构造函数:就是在开发者不给任何数据的情况下,编译器会默认给出一个参数,类似于之前给的是变量,现在构造函数的参数全是默认参数 。还是给个例子吧

//全缺省构造参数
student(const char*name="张三",int _age,double _score=90)
{
  strcpy(name,_name);
  age=_age;
  score=_score;
}
//无参构造函数
student()
{
}

看上面这个例子,此时直接定义一个student m1;此时它既可以去调全缺省的构造函数,又可以去调无参构造,这是不是在编译层面的重定义

探究编译器默认给出的构造函数到底有没有用

class student
{
public:
	student()
	{
		cout << "student 调用" << endl;
		strcpy(name, "李四");
		age = 20;
		score = 91.4;
	}
	student(const char* _name, int _age, double _score)
	{
		strcpy(name, _name);
		age = _age;
		score = _score;
	}
	void print()
	{
		cout << "name" << name << endl;
		cout << "年龄" << age << endl;
		cout << "成绩" << score << endl;
	}
private:
	char name[15];
	int age;
	double score;
};
class person
{
public:
	
private:
	char id[20];
	student m1;

};
int main()
{
	//student m1("张三", 18, 90.0);
	person m2;//m2中没有给任何构造函数,但编译器会调用person()构造函数
	system("pause");
	return 0;
}

编译器做了什么?
首先,编译器会调用person()的构造函数(编译器给出的,不是自定义的),因为person类中有student m1这个对象,既然是对象,那它就需要初始化,此时我们给student()对象中的构造函数让它打印一条语句(主要是为了验证person()有没有去调student()的构造函数),看结果:可以看到已经被初始化了。
在这里插入图片描述

构造函数的调用顺序

1.特性:如果类中含有其他类对象,则先调用的是其他类的构造函数,再调用自身的构造函数

class student
{
public:
	student()
	{
		cout << "student 调用" << endl;
		strcpy(name, "李四");
		age = 20;
		score = 91.4;
	}
	student(const char* _name, int _age, double _score)
	{
		strcpy(name, _name);
		age = _age;
		score = _score;
	}
	void print()
	{
		cout << "name" << name << endl;
		cout << "年龄" << age << endl;
		cout << "成绩" << score << endl;
	}
private:
	char name[15];
	int age;
	double score;
};
class person
{
public:
	person()
	{
		cout << "person类的调用" << endl;
	}
private:
	char id[20];
	student m1;

};
int main()
{
	//student m1("张三", 18, 90.0);
	person m2;
	system("pause");
	return 0;
}

在这里插入图片描述
2.假如有多个不同的类对象定义在一个类中,则此时的调用顺序与定义顺序一样。

默认析构函数

析构函数特征:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值。
  3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
    5.析构函数的顺序与构造函数的顺序是相反的
class student
{
public:
	student()
	{
		strcpy(name, "张三");
		age = 100;
		score = 90.0;
		cout << "student 构造调用" << endl;
	}
	~student()
	{
		cout << "student 析构调用" << endl;
	}
private:
	char name[15];
	int age;
	double score;
};
class person
{
public:
	person()
	{
		cout << "person 构造的调用" << endl;

		strcpy(id, "1900201");
	}
	~person()
	{
		cout << "person 类型析构的调用" << endl;
	}
private:
	char id[20];
	student m1;

};
void test()
{ 
	person p1;
}
int main()
{
	test();
	system("pause");
	return 0;
}

这里我们可以看到析构函数的调用顺序与构造函数的调用函数是相反的。
在这里插入图片描述

默认拷贝构造函数

特征:
1.拷贝构造函数是构造函数的一个重载形式
2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用
3.若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
4.

class person
{
public:
	person(const char* _name, int _age)
	{
		strcpy(name, _name);
		age = _age;
		cout << "构造函数的调用" << this<<endl;
	}
	~person()
	{
		cout << "析构函数的调用" << this<<endl;
	}
	void show()
	{
		cout << "姓名  " << name << endl;
		cout << "年龄   " << age << endl;
	}
private:
	char name[15];
	int age;
};
void test()
{
	person m1("张三", 20);
	person m2(m1);

	m1.show();
	m2.show();
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述

默认拷贝构造函数的弊端

class student
{
public:
	//构造函数
	student(const char* str, int _age, double _score)
	{
		int len = strlen(str);
		name = (char*)malloc(len + 1);
		if (name == nullptr)
		{
			cout << "内存开辟失败,初始化错误" << endl;
			return;
		}
		strcpy(name, str);
		age = _age;
		score = _score;
	}
	//析构函数
	~student()
	{
		cout << "析构函数" << endl;
		cout << &(name) << endl;
		if (name == nullptr)
		{
			return;
		}
		free(name);
		name = nullptr;

	}
	//打印函数
	void show()
	{
		cout << "打印函数" << endl;
		cout << &(name) << endl;
		cout << "姓名" << "  " << name << endl;
		cout << "年龄" << "  " << age << endl;
		cout << "成绩" << "  " << score << endl;
	}
private:
	char* name;
	int age;
	double score;
};
void test01()
{
	student m1("张三", 20, 98.5);
	//调用默认拷贝的两种方式
	student m2(m1);

	m1.show();
	m2.show();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述
所以可得出:当一个类中涉及到资源管理时,拷贝构造必须显式的给出
当一个类没有涉及到内存管理时,此时大可使用c++中的默认拷贝构造函数。
探究深拷贝与浅拷贝:
简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误
所以说,当存在资源管理时,必须显式给出自定义的拷贝构造函数。

class student
{
public:
	//构造函数
	student(const char* str, int _age, double _score)
	{
		int len = strlen(str);
		name = (char*)malloc(len + 1);
		if (name == nullptr)
		{
			cout << "内存开辟失败,初始化错误" << endl;
			return;
		}
		strcpy(name, str);
		age = _age;
		score = _score;
	}
	//拷贝构造函数,加const原因是为了避免在拷贝时的错误修改
	student(const student& p1)
	{
		name = (char*)malloc(strlen(p1.name) + 1);
		if (name == NULL)
		{
			cout << "内存开辟失败,拷贝退出" << endl;
			return;
		}
		strcpy(name, p1.name);
		age = p1.age;
		score = p1.score;
	}
	//析构函数
	~student()
	{
		cout << "析构函数" << endl;
		cout << &(name) << endl;
		if (name == nullptr)
		{
			return;
		}
		free(name);
		name = nullptr;

	}
	//打印函数
	void show()
	{
		cout << "打印函数" << endl;
		cout << &(name) << endl;
		cout << "姓名" << "  " << name << endl;
		cout << "年龄" << "  " << age << endl;
		cout << "成绩" << "  " << score << endl;
	}
private:
	char* name;
	int age;
	double score;
};
void test01()
{
	student m1("张三", 20, 98.5);
	//调用默认拷贝的两种方式
	student m2(m1);

	m1.show();
	m2.show();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

调用拷贝构造函数的时机

1.第一种,就像我们上面给的那样,需要将另一个对象的值赋给新定义出来的对象,此时需要拷贝构造函数
2.如果以对象类作为返回值的话,此时也必须调用拷贝函数

class student
{
public:
	//构造函数
	student(const char* str, int _age, double _score)
	{
		cout << "构造函数" << endl;
		int len = strlen(str);
		name = (char*)malloc(len + 1);
		if (name == nullptr)
		{
			cout << "内存开辟失败,初始化错误" << endl;
			return;
		}
		strcpy(name, str);
		age = _age;
		score = _score;
	}
	//拷贝构造函数,加const原因是为了避免在拷贝时的错误修改
	student(const student& p1)
	{
		cout << "拷贝构造函数调用" << endl;
		name = (char*)malloc(strlen(p1.name) + 1);
		if (name == NULL)
		{
			cout << "内存开辟失败,拷贝退出" << endl;
			return;
		}
		strcpy(name, p1.name);
		age = p1.age;
		score = p1.score;
	}
	//析构函数
	~

以上是关于C++类和对象中的主要内容,如果未能解决你的问题,请参考以下文章

Qt C++ 项目中 Xamarin 项目中代码的可重用性

关于网友博客中代码的可用性问题

继承中代码的执行顺序

Tutorial中代码的区别及不同效果

Java中代码块执行顺序

C++笔记对C++的简单总结