【C++】类与对象的学习(中)

目录

一、默认成员函数:

二、构造函数:

1、定义:

2、理解:

三、析构函数:

1、定义:

2、理解:

四、拷贝构造:

1、定义:

2、理解:

五、运算符的重载:

1、一般运算符的重载:

2、赋值运算符的重载:

1、格式:

2、注意:


一、默认成员函数:

一个类中,里面什么都不写的话就叫做空类,但在其中并不是什么都没有的,编译器会自动生成6个默认成员函数。

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

二、构造函数:

1、定义:

构造函数是一个特殊的成员函数,它的名字必须和类名相同,创建类类型对象时,由编译器自动调用,,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

这是每次创建一个实例时,这个函数都会调用一次。

2、理解:

1、函数名与类名相同。

2、无返回值(也不用写void)。

3、对象实例化的时候,编译器自动调用对应的构造函数。

接下来用一串代码理解上述3条特征。

cpp 复制代码
class Date
{
public:
	Date()
	{
		_year = 2;
		_month = 2;
		_day = 2;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Print();
	return 0;
}

上述代码中,Date()这个就是一个构造函数,和类名相同,也不用写任何返回值,可以看到运行后的结果是已经调用构造函数Date后的值
4、函数构造可以进行重载。

注意:无参构造函数的调用后面不加(),这样和函数的声明会产生歧义。

cpp 复制代码
class Date
{
public:
	Date()
	{
		cout << "无参数构造函数的调用" << endl;
		_year = 2;
		_month = 2;
		_day = 2;
	}
	Date(int year, int month, int day)
	{
		cout << "有三个参数构造函数的调用" << endl;
		_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(2024,7,4);//调用带参数的构造函数
	d2.Print();
	return 0;
}

5、如果类中没有显式定义构造函数,那么C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
**6、**首先要知道:

C++把类型分为了两种:

a、内置类型(基本类型):int/char/double...

b、自定义类型:struct/class/union...

如果我们不写构造函数,那么编译器就会默认生成构造函数,此时这个默认构造函数会对内置类型不做任何处理(虽然有些编译器会进行处理,但那是个性化的,不是所有的编译器都会处理),自定义类型就会去调用它的默认构造。

cpp 复制代码
class Date
{
public:
	Date()
	{
		cout << "无参数构造函数的调用by Date" << endl;
		_year = 2;
		_month = 2;
		_day = 2;
	}
	Date(int year, int month, int day)
	{
		cout << "有三个参数构造函数的调用by Date" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};


class Stu
{
private:
	char name[20];
	int age;
	char id[15];

	Date d1;
};

int main()
{
	Stu s1;
	return 0;
}

如上所示:

实例化Stu这个类,但是发现没有写构造函数,就会默认生成个构造函数,那些内置类型不做处理,自定义类型d1就会调用它的默认构造(Date())。

如下图都是内置类型,就不会做任何处理。

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

理解:不传参就可以调用的是默认构造函数

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

是默认构造函数。

所以:

1、一般情况下,有内置类型成员,就需要自己写构造函数,不能用编译器自己生成。

2、如果全部都是自定义类型的成员,那么就可以考虑使用编译器自己生成的构造函数。

ps:

在C++11中,在成员变量声明的时候可以给缺省值。

如上所示:

在Date这个类中,可以给默认缺省值,给编译器生成默认构造函数用。

注意:

C++11以后才支持的,这里是进行声明(因为没有创建空间),不是初始化!!!
对于构造函数:

一般情况下都需要自己写,

但是,下面两种情况可以不用自己写:

a、内置成员都有缺省值,且初始化符合我们的要求。

b、全是自定义类型的构造,且这些类型都定义默认构造。

三、析构函数:

1、定义:

析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象销毁时会自动调用析构函数,完成对象中资源的清理工作。

2、理解:

1、析构函数名就是类名前面加上一个"~";

2、和构造函数一样没有参数返回类型 ,并且没有参数 ,所以不能够重载

3、 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数,在程序结束的时候自动调用;

4、自动生成的默认析构函数,对内置类型不做处理,自定义类型会调用其析构函数。
那么什么时候可以显示定义析构函数,什么时候可以不用显示定义析构函数呢:

1、一般,当存在动态申请资源的时候就需要显示定义;

2、当没有动态申请资源的时候就不用显示定义;

3、当需要释放资源的成员变量都是自定义类型的时候,不用写析构,这个自定义类型就会调用它的析构函数。

cpp 复制代码
#include<iostream>
using std::cout;
using std::endl;

class Stack
{
public:
	Stack()
	{
		cout << "Stack()已经调用" << endl;
		_a = (int*)malloc(sizeof(int) * 4);
		if (_a == nullptr)
		{
			perror("stack malloc fail");
			return;
		}
		_capacity = 4;
		_top = 0;
	}

	Stack(int capacity)
	{
		cout << "Stack(int capacity)已经调用" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("stack malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

	~Stack()
	{
		cout << "~Stack()已经调用" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _a =  nullptr;
	int _top = 0;
	int _capacity;
};

int main()
{
	Stack s1;
	Stack s2(10);
	return 0;
}

如上~Stack()就是一个析构函数,当对象销毁时就会自动调用。

四、拷贝构造:

首先要了解:C++规定,传参赋值中:创建一个已有对象一模一样的新对象,内置类型直接拷贝,传值的自定义类型必须调用拷贝构造完成拷贝(深浅拷贝有关系)

1、定义:

首先要了解到,拷贝引入是在创建对象的时候,创建一个已有对象一模一样的新对象。

它只有一个形参,该形参是我要复制的对象(一般可以用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

2、理解:

1、拷贝构造是构造函数的一种重载形式;

2、拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

3、当没有显示定义拷贝构造函数的时候,默认拷贝构造会对内置类型成员完成值拷贝

自定义类型成员会调用它的拷贝构造

此时在日期类中,可以不用写默认拷贝拷贝构造,正常拷贝,但是有些情况,比如在栈这个类中,如果使用编译器给的默认拷贝构造,会存在问题,会拷贝出同一个指向同一空间的指针,这时如果析构的话,会对同一块空间释放两次,这时程序就会崩溃了。

针对上述为题,我们就需要自己实现拷贝构造,实现深拷贝来解决上述问题。

就要完成开辟一块空间,然后在这块空间里面,拷贝那些值。

cpp 复制代码
class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 7, 13);
	Date d2(d1);
	return 0;
}

上述就是浅拷贝的例子。

接下来,对这个栈实现深拷贝:

cpp 复制代码
	Stack(Stack& st)
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (_a == nullptr)
		{
			perror("stack malloc fail");
			return;
		}
		memcpy(_a, st._a, sizeof(int) * st._capacity);
		_top = st._top;
		_capacity = st._capacity;
	}

五、运算符的重载:

1、一般运算符的重载:

C++相对于C语言,增加了运算符重载。使用关键字operator后面加上要重载的运算符。

函数名:operator<

函数原型:返回值类型 operator操作符(参数列表)

注意:

1、不能通过连接其他符号来创建新的操作符:比如operator@(原来没有@这个操作符)

2、重载操作符必须有一个自定义型参数,不能都是内置类型,毕竟是比较自定义类型的,可以自定义类型和内置类型同时存在。

3、用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义

4、作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

5、 .* :: sizeof ?: . 注意以上5个运算符不能重载。

cpp 复制代码
bool operator<(const Date& x)
{
	if (_year > x._year)
	{
		return false;
	}
	else if (_year == x._year && _year > x._year)
	{
		return false;
	}
	else if (_year == x._year && _year > x._year && _day > x._day)
	{
		return false;
	}
	return true;
}

以上就是重载<运算符,_year实际上是this->_year,但是如果写在全局域中,那么就会无法访问私有成员,解决办法可以用友元,或者把operator<写成成员函数。

2、赋值运算符的重载:

1、格式:

1、参数类型:const 类名&,传递引用可以提高传参效率

2、返回值类型:类名&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

3、返回*this :要复合连续赋值的含义

2、注意:

赋值运算符只能重载成类的成员函数不能重载成全局函数,因为赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

在这默认生成赋值重载和拷贝构造行为一样,

对内置类型成员进行值拷贝/浅拷贝,

对自定义类型成员会调用它的赋值重载。

cpp 复制代码
Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return (*this);
}

以上就是在日期类的重载赋值符,这个与拷贝构造不一样,拷贝构造是通过一个类创造一个一样的类,而这个是将一个已经存在的类赋值给另一个已经存在的类。

相关推荐
小俊俊的博客1 小时前
海康RGBD相机使用C++和Opencv采集图像记录
c++·opencv·海康·rgbd相机
_WndProc1 小时前
C++ 日志输出
开发语言·c++·算法
薄荷故人_1 小时前
从零开始的C++之旅——红黑树及其实现
数据结构·c++
m0_748240021 小时前
Chromium 中chrome.webRequest扩展接口定义c++
网络·c++·chrome
qq_433554541 小时前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
努力学习编程的伍大侠1 小时前
基础排序算法
数据结构·c++·算法
yuyanjingtao2 小时前
CCF-GESP 等级考试 2023年9月认证C++四级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
闻缺陷则喜何志丹2 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
charlie1145141913 小时前
C++ STL CookBook
开发语言·c++·stl·c++20
小林熬夜学编程3 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http