【C++】:类和对象(2)

news2024/11/17 5:50:23

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux的基础知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

 

目录

1. 类的6个默认成员函数

2. 构造函数

2.1概念

2.2特性 

3. 析构函数

3.1概念

3.2特性

4. 拷贝构造

4.1概念

4.2特性

5. 赋值运算符重载

5.1运算符重载

5.2赋值运算符重载

5.3前置++、后置++重载

6. const成员

7. 取地址及const取地址操作符重载


1. 类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
//空类
class Date {};

2. 构造函数

2.1概念

构造函数主要完成初始化工作

对于下面这个日期类:

class Date
{
public:
	//初始化
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1, d2;
	d1.Init(2023, 10, 1);
	d1.Print();

	d2.Init(2023, 10, 2);
	d2.Print();
	return 0;
}

 

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2特性 

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
        1. 函数名与类名相同。
        2. 无返回值
        3. 对象实例化时编译器自动调用对应的构造函数。
        4. 构造函数可以重载
class Date
{
public:

	//带参的构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//无参的构造函数
	Date(){}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void TestDate()
{
	Date d1;  //调用无参的构造函数

	Date d2(2023, 10, 1);//调用带参的构造函数

	//Date d3();    //调用无参的构造函数创建对象时不能带(),否则就变成了函数声明
}
5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
class Date
{
public:

	/*
	//带参的构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//无参的构造函数
	Date(){}
	*/

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1;
	return 0;
}

当我们屏蔽掉我们自己写两个构造函数之后,在创建对象时会使用编译器自动生成的构造函数来完成对应的初始化,如果我们自己已经写了构造函数,则不再会自动生成。

class Date
{
public:

	//带参的构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1;
	Date d2(2023, 10, 1);
	return 0;
}

6. 关于编译器生成的默认成员函数:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的 默认构造函数并没有什么用??
解答:由于C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char/int*/char*...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数:
class Time
{
public:
	Time()
	{
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
    //内置类型
	int _year;
	int _month;
	int _day;
    
    //自定义类型
	Time _t;
};

int main()
{
	Date d1;
	return 0;
}

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在
类中声明时可以给默认值
class Time
{
public:
	Time()
	{
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	//内置类型
    //C++11支持在声明时给内置类型缺省值
	int _year = 1949;
	int _month = 10;
	int _day = 1;

	//自定义类型
	Time _t;
};
为了代码的实用性。所以我们在写构造函数时写为全缺省是一种非常不错的选择,当我们进行无参创建时会自动使用我们给的缺省值,而并不是随机值。
class Date
{
public:
	//全缺省构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//内置类型
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(2023, 10, 1);
	return 0;
}

7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
默认构造函数
多个并存就会产生调用二义性,不传参调用的就是默认构造
class Date
{
public:
	//全缺省构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//无参构造函数
	Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	//内置类型
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	return 0;
}

这段代码首先全缺省的构造函数和无参的构造函数形成了函数重载,但是在定义对象时进行了一个无参的调用,这时就会出现一个函数调用出现冲突的问题,由于是无参,既可以调用全缺省的,也可以调用午无参的,那么编译器就陷入了困难,所以尽量避免无参和全缺省的构造函数同时出现。

总结:

编译器生成的默认构造:

1. 我们不写,才会自动生成

2. 不会对内置类型进行处理(C++11支持对内置类型声明时给缺省值)

3. 自定义类型才会处理,会去调用这个成员的默认构造函数

当成员变量都是自定义类型时,可以考虑不写构造函数

3. 析构函数

3.1概念

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

3.2特性

1. 析构函数名是在类名前加上字符 ~
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
typedef int DateType;
class Stack
{
public:
	//构造函数
	Stack(size_t capacity)
	{
		_array = (DateType*)malloc(sizeof(DateType) * capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}
	void STPush(DateType x)
	{
		//...
	}
	//析构函数
	~Stack()
	{
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}
private:
	DateType* _array;
	size_t _capacity;
	size_t _size;
};

int main()
{
	Stack st(1);
	return 0;
}

5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?我们可以采用程序来观察一下:

typedef int DateType;
class Stack
{
public:
	//构造函数
	Stack()
	{
		_array = (DateType*)malloc(sizeof(DateType) * 4);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = 4;
	}
	void STPush(DateType x)
	{
		//...
	}
	
private:
	DateType* _array;
	size_t _capacity;
	size_t _size;
};

int main()
{
	Stack st;
	return 0;
}

可以看到编译器默认生成的析构函数也是不会对内置类型做处理,那么当遇见自定义类型它会如何呢?

typedef int DateType;
class Stack
{
public:
	//构造函数
	Stack()
	{
		_array = (DateType*)malloc(sizeof(DateType) * 4);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = 4;
	}
	void STPush(DateType x)
	{
		//...
	}
	//析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}
private:
	DateType* _array;
	size_t _capacity;
	size_t _size;
};


class Date
{
public:
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:

	//内置类型
	int _year;
	int _month;
	int _day;

	//自定义类型
	Stack st;
};
int main()
{
	//Stack st;
	Date d1;
	return 0;
}

在这个过程中main函数 中创建了 Date 对象 d1 ,而 d1 中包含 4 个成员变量,其中 _year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而st是Stack类对象,所以在d1销毁时,要将其内部包含的Stack类的st 对象销毁,所以要调用Stack类的析构函数。但是:
main函数中不能直接调用Stack类的析构函数,实际要释放的是Date 类对象,所以编译器会
调用 Date类的析构函数,而Date 没有显式提供,则编译器会给 Date类生成一个默认的析构函
数,目的是在其内部调用Stack类的析构函数,即当Date对象销毁时,要保证其内部每个自定
义对象都可以正确销毁main函数中并没有直接调用Stack类析构函数,而是显式调用编译器
为Date 类生成的默认析构函数。
注意:创建哪个类的对象则调用该类的构造函数,销毁那个类的对象则调用该类的析构函数
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

4. 拷贝构造

4.1概念

在日常生活中,我们发现有两个人长得几乎一模一样,我们称他们为双胞胎,那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

拷贝构造函数只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用

4.2特性

首先我们可以看这样一段代码:
typedef int DateType;
class Stack
{
public:
	//构造函数
	Stack(size_t capacity = 3)
	{
		cout << "Stack()" << endl;
		_array = (DateType*)malloc(sizeof(DateType) * capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}

	//析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}
private:
	DateType* _array;
	size_t _capacity;
	size_t _size;
};

void func(Stack s)
{
	//...
}

int main()
{
	Stack s1;
    //函数调用进行了一份拷贝
	func(s1);
	return 0;
}

首先这段代码是会报错的,因为s1是Stack类的对象,当func函数调用s1的时候,会拷贝一份s1,最主要的是拷贝的s与s1是同一块空间,就代表着两个类对象同时指向同一个空间,那么在进行析构函数清理资源的时候,无论先清理哪一个,剩下的一个指向的就是一块已经被释放的空间,就会对同一块空间进行多次释放,造成程序崩溃,那么就需要拷贝构造函数来解决这个问题:

拷贝构造函数也是特殊的成员函数,其特征如下:
1. 拷贝构造函数是构造函数的一个重载形式
2. 拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
class Date
{
public:
	//构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		cout << "Date()" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造
	//Date(const Date d)  不能使用传值调用
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	//内置类型
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

在这里说一下在传值调用的时候为什么会发生无穷递归:首先要明白一个点,传值就需要进行拷贝,拷贝就需要调用拷贝构造。

3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

class Date
{
public:
	//构造函数
	Date(int year = 1949, int month = 10, int day = 1)
	{
		cout << "Date()" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//内置类型
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

那么拷贝构造对于自定义类型如何呢?

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	//构造函数
	Date(int year = 1949, int month = 10, int day = 1)
	{
		cout << "Date()" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//内置类型
	int _year;
	int _month;
	int _day;

	//自定义类型
	Time _t;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式(浅拷贝)直接拷贝的,而自定
义类型是调用其拷贝构造函数完成拷贝的

 4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,如果没有资源的创建于释放是不需要自己写拷贝构造的,如果有资源的创建于释放是需要自己写。

typedef int DateType;
class Stack
{
public:
	//构造函数
	Stack(size_t capacity = 3)
	{
		cout << "Stack()" << endl;
		_array = (DateType*)malloc(sizeof(DateType) * capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}	
	//析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}
private:
	DateType* _array;
	size_t _capacity;
	size_t _size;
};

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

程序崩溃了,那么是什么原因呢?

由于编译器默认生成的拷贝构造函数都是浅拷贝(值拷贝),所以导致了s1和s2指向的是同一块空间,那么在进行s2的析构函数清理资源的时候,先清理了s2,所以s1和s2指向的这同一块空间已经被释放了,但是s1的析构函数并不知道,还会对这块空间进行释放,那么对同一块空间的多次释放就会造成程序崩溃

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请
时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

 

typedef int DateType;
class Stack
{
public:
	//构造函数
	Stack(size_t capacity = 3)
	{
		cout << "Stack()" << endl;
		_array = (DateType*)malloc(sizeof(DateType) * capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}	
	//深拷贝
    //拷贝构造
	Stack(const Stack& s)
	{
		cout << "Stack(const Stack& s)" << endl;
		_array = (DateType*)malloc(sizeof(DateType) * s._capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_array, s._array, sizeof(DateType) * s._capacity);
		_size = s._size;
		_capacity = s._capacity;
	}
	//析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}
private:
	DateType* _array;
	size_t _capacity;
	size_t _size;
};

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

5. 拷贝构造函数典型调用场景:
  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

5. 赋值运算符重载

5.1运算符重载

C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)
注意:
  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为藏的this
  • .*  ::  sizeof  ?:  .  注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

1. 全局的运算符重载


//全局的operator==
bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

//全局的operator!=
bool operator!=(const Date& d1, const Date& d2)
{
	return !(d1 == d2);
}

//全局的operator<
bool operator<(const Date& d1, const Date& d2)
{
	if (d1._year < d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month < d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month == d2._month && d1._day < d2._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//全局的operator<=
bool operator<=(const Date& d1, const Date& d2)
{
	return d1 < d2 || d1 == d2;
}

//全局的operator>
bool operator>(const Date& d1, const Date& d2)
{
	return !(d1 <= d2);
}

//全局的operator>=
bool operator>=(const Date& d1, const Date& d2)
{
	return !(d1 < d2);
}

//获取天数
int GetMonth_day(int year, int month)
{
	int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

	if (month == 2 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
	{
		return 29;
	}
	return monthArray[month];
}

//全局的operator+=
Date& operator+=(Date& d, int day)
{
	d._day += day;
	while (d._day > GetMonth_day(d._year, d._month))
	{
		//天满了进月
		d._day -= GetMonth_day(d._year, d._month);
		d._month++;

		//月满了进年,月置为1
		if (d._month == 13)
		{
			++d._year;
			d._month = 1;
		}
	}
	return d;
}

//全局的operator+
Date operator+(Date& d, int day)
{
	Date tmp(d);
	tmp += day;
	return tmp;
}

2. 成员函数的运算符重载

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

class Date
{
public:
	//全缺省构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	///运算符重载/
	//成员函数operator==
	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}

	//成员函数operator!=
	bool operator!=(const Date& d)
	{
		return !(*this == d);
	}

	//成员函数operator<
	bool operator<(const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	//成员函数operator<=
	bool operator<=(const Date& d)
	{
		return *this < d || *this == d;
	}

	//成员函数operator>
	bool operator>(const Date& d)
	{
		return !(*this <= d);
	}

	//成员函数operator>=
	bool operator>=(const Date& d)
	{
		return !(*this < d);
	}

	//获取天数
	int GetMonth_day(int year, int month)
	{
		int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

		if (month == 2 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
		{
			return 29;
		}
		return monthArray[month];
	}

	//成员函数operator+=
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonth_day(_year, _month))
		{
			//天满了进月
			_day -= GetMonth_day(_year, _month);
			_month++;

			//月满了进年,月置为1
			if (_month == 13)
			{
				++_year;
				_month = 1;
			}
		}
		return *this;
	}

	//成员函数operator+
	Date operator+(int day)
	{
		Date tmp(*this);
		tmp += day;
		return tmp;
	}
private:  
	int _year;
	int _month;
	int _day;
};

5.2赋值运算符重载

1. 赋值运算符重载格式

①参数类型:const T&,传递引用可以提高传参效率
②返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测③是否自己给自己赋值

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


class Date
{
public:
	//全缺省构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	//成员函数operator=
	Date& operator=(const Date& d)
	{
		//自己不能给自己赋值
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:  
	int _year;
	int _month;
	int _day;
};

2. 赋值运算符只能重载成类的成员函数不能重载成全局函数
 

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
	if (&left != &right)
	{
		left._year = right._year;
		left._month = right._month;
		left._day = right._day;
	}
	return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

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

3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
 

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time& operator=(const Time& t)
	{
		if (this != &t)
		{
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}
		return *this;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date1
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date1 d1;
	Date1 d2;
	d1 = d2;
	return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?
如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要自己实现。

5.3前置++、后置++重载

根据前置++和后置++的区别:先使用后++还是先++后使用可以来进行设计:

1. 前置++


class Date
{
public:
	//全缺省构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	//成员函数operator++(前置)
	Date& operator++()
	{
		_day += 1;
		return *this;
	}

private:  
	int _year;
	int _month;
	int _day;
};

前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载

C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递

注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1

2. 后置++


class Date
{
public:
	//全缺省构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	//成员函数operator++(后置)
	Date operator++(int)
	{
		Date tmp(*this);
		_day += 1;
		return tmp;
	}
private:  
	int _year;
	int _month;
	int _day;
};

6. const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Print() const
	{
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
void Test()
{
	Date d1(2023, 1, 1);
	d1.Print();
	const Date d2(2023, 1, 1);
	d2.Print();
}

int main()
{
	Test();
	return 0;
}

1. const对象可以调用非const成员函数吗?

不能,const对象权限小于非const对象,权限不能放大。

2. 非const对象可以调用const成员函数吗?

可以,权限缩小。

3. const成员函数内可以调用其它的非const成员函数吗?

不能,const成员函数它承诺不会修改对象的状态。因此,const成员函数不能调用非const成员函数,因为非const成员函数可以修改对象的状态,权限的放大。

4. 非const成员函数内可以调用其它的const成员函数吗?

非const成员函数内可以调用其它的const成员函数。非const成员函数没有限制对对象的修改,因此可以调用const成员函数,而const成员函数不会修改对象的状态,与非const成员函数的调用不冲突。这种情况下,调用const成员函数不会违反const成员函数的承诺。

7. 取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
 

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print() const
	{
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}

    //非const版本
	Date* operator&()
	{
		return this;
	}
    
    //const版本
	const Date* operator&()const
	{
		return this;
	}

private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063394.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

投资理财:利率下行时代应该怎样存钱?

大家好&#xff0c;我是财富智星&#xff0c;今天跟大家分享一下当下利率下行的时代&#xff0c;钱应该怎样存&#xff0c;存哪里的问题。 一、 银行利率下行 在过去的三十年里&#xff0c;您已经逐渐适应了不断下降的利率&#xff0c;从10%到现在的1.65%。而在未来&#xff0c…

vue3 中使用echarts图表——柱状图

柱状图是比较常用的图形结构&#xff0c;所以我先收集一些精美的柱状图 一、柱状图&#xff1a;设置圆角和颜色 <template><div class"box" ref"chartDom"></div> </template> <script setup> import { ref, onMounted } fr…

C中volatile总结

在CPU处理过程中&#xff0c;需要将内存中的数据载入到寄存器中才能计算&#xff0c;所以可能涉及到一个问题&#xff0c;如果内存中的数据被更改了&#xff0c;但是寄存器还是使用的旧数据&#xff0c;这样就会造成数据的不同步。 一、volatile关键字的作用 使用volatile关键…

中级工程师职称评审中业绩材料具体有哪些呢?甘建二告诉你

十年职称甘建二&#xff0c;一门心思只聊工程师职称评定。 关注我&#xff0c;轻松拿到职称。 中级工程师职称评审中需要准备的评审材料很多&#xff0c;业绩、论文、技术总结、评审表、社保、单位等各类材料&#xff0c;其中难倒大家的就是业绩&#xff0c;业绩到底指的是什么…

【初识Linux】Linux环境配置、Linux的基本指令 一

Linux基本指令一 一、学习前提(环境配置&#xff09;①安装Xshell和云服务器推荐②Xshell用途如下图③打开Xshell 二、 Linux基本指令①whoami和who指令②pwd、ls、ls -l三个指令ls指令扩充 ③cd指令前提了解有了上面的认识&#xff0c;我们就可以开始cd指令的学习了 ④tree指令…

从零开始的C++(五)

1.类和对象的补充 当对象是const修饰的常量时&#xff0c;形参中的this是隐含的&#xff0c;那么该如何写函数才能传常量对象呢&#xff1f;如果还是按照正常的方式写&#xff0c;则会出现实参是const修饰的&#xff0c;形参没有&#xff0c;出现了权限的扩大&#xff0c;无法…

基于卷积神经网络的法线贴图生成器

在本文中&#xff0c;我们将学习如何训练卷积神经网络从彩色图像生成法线贴图。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 1、数据和工具 我们正着手训练神经网络从彩色图像生成法线贴图。 我们将以“成对”的方式做到这一点。 这意味着我们将显示相应图像的网络对…

程序人生 / 散文分享 / 生活感悟——【追光的日子】《爷爷的12本日历》,若你也共情,欢迎在评论区分享你的故事、观点、感悟和思考!

在一切变好之前,我们总要经历一些不开心的日子,这段日子也许很长,也许只是一觉醒来。有时候,选择快乐,更需要勇气。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3]…

Spring Cloud OpenFeign 性能优化的4个方法

OpenFeign 是 Spring 官方推出的一种声明式服务调用和负载均衡组件。它的出现就是为了替代已经进入停更维护状态的 Netflix Feign&#xff0c;是目前微服务间请求的常用通讯组件。 1.超时设置 OpenFeign 底层依赖Ribbon 框架&#xff0c;并且使用了 Ribbon 的请求连接超时时间…

打表找规律与分析判断:ARC144C

https://atcoder.jp/contests/arc144/tasks/arc144_c?langen 一开始我猜的结论是前后 k k k 个预处理&#xff0c;中间贪心。 通过打表&#xff1a; 可以发现是前面 2 k 2k 2k 连续块直接暴配&#xff0c;最后一段再用我想的贪心。 究其原因&#xff0c;其实是我们本质上…

安卓开发中遇到的奇奇怪怪的问题(四)

好久没有写这个系列了&#xff0c;感觉还是需要把日常开发中遇到的问题做一个记录总结&#xff0c;因为有些问题我当时遇到时&#xff0c;搜都搜不到&#xff0c;只能慢慢摸索。帮助他人的同时也能给自己留个备忘录。话不多说&#xff0c;凡是近一年的奇怪问题&#xff0c;我想…

Rust中的枚举和模式匹配

专栏简介&#xff1a;本专栏作为Rust语言的入门级的文章&#xff0c;目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言&#xff0c;虽然历史没有C、和python历史悠远&#xff0c;但是它的优点可以说是非常的多&#xff0c;既继承了C运行速度&#xff0c;还拥有了Java…

24 mysql all 查询

前言 这里主要是 探究一下 explain $sql 中各个 type 诸如 const, ref, range, index, all 的查询的影响, 以及一个初步的效率的判断 这里会调试源码来看一下 各个类型的查询 需要 lookUp 的记录 以及 相关的差异 此系列文章建议从 mysql const 查询 开始看 测试表结构…

【网络】路由器和交换机的区别

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1…

小谈设计模式(21)—迭代器模式

小谈设计模式&#xff08;21&#xff09;—迭代器模式 专栏介绍专栏地址专栏介绍 迭代器模式对象分析聚合对象&#xff08;Aggregate&#xff09;迭代器对象&#xff08;Iterator&#xff09; Java程序示例程序分析12 优缺点分析优点简化了聚合对象的接口统一的遍历方式增加了代…

20秒基于Chat GPT完成工作中的小程序

1. 写在前面 GPT自从去年爆发以来&#xff0c;各大公司在大模型方面持续发力&#xff0c;行业大模型也如雨后春笋一般发展迅速&#xff0c;日常工作中比较多的应用场景还是问答模式&#xff0c;作为写程序的辅助也偶尔使用。今天看到一篇翻译的博客“我用 ChatGPT&#xff0c;…

更新Xcode 版本后运行项目出现错误 Unable to boot the Simulator 解决方法

错误截图 出现 Unable to boot the Simulator 错误原因很多&#xff0c;以下方法不一定都适用&#xff0c;我是通过以下方法解决的 打开命令终端输入以下命令&#xff0c;可能需要你输入开机密码 sudo rm -rf ~/Library/Developer/CoreSimulator/Caches

Android---字节码层面分析Class类文件

Java 提供了一种可以在所有平台上都能使用的一种中间代码---字节码文件(.class文件)。有了字节码&#xff0c;无论是那个平台只要安装了虚拟机都可以直接运行字节码文件。有了虚拟机&#xff0c;解除了 java 虚拟机与 java 代码之间的耦合。 Java 虚拟机当初被设计出来时就不单…

【设计模式】访问者模式

文章目录 1.访问者模式定义2.访问者模式的角色3.访问者模式实战案例3.1.场景说明3.2.UML类图3.3.代码实现 4.访问者模式优缺点5.访问者模式适用场景6.访问者模式总结 主页传送门&#xff1a;&#x1f481; 传送 1.访问者模式定义 访问者模式&#xff08;Visitor Pattern&#x…

【MATLAB源码-第43期】基于matlab的turbo码误码率仿真比较不同迭代次数,采用logmap/sova算法。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 Turbo码是一种前向纠错码 (Forward Error Correction, FEC)&#xff0c;在 1993 年由法国的两位研究员 Claude Berrou 和 Alain Glavieux 提出。这种编码技术以其接近 Shannon 极限的高性能而受到广泛关注。以下是关于 Turbo…