【C++初阶】类与对象(二)

news2024/10/7 12:25:12

目录

  • 前言:
  • 一、构造函数
    • 1.1 构造函数概念
    • 1.2 为什么有构造函数
    • 1.3 构造函数的写法及使用
    • 1.4 默认构造函数
    • 1.5 哪些可为默认构造函数
  • 二、析构函数
    • 2.1 析构函数概念
    • 2.2 为什么有析构函数
    • 2.3析构函数的写法及使用
    • 2.4 默认析构函数
  • 三、拷贝构造函数
    • 3.1 拷贝构造函数概念
    • 3.2 为什么有拷贝构造函数
    • 3.3 拷贝构造函数的写法及使用
    • 3.4 默认拷贝构造函数
  • 四、赋值运算符重载
    • 4.1 运算符重载
    • 4.2 赋值运算符重载
      • 4.2.1 赋值运算符重载概念
      • 4.2.2 赋值运算符重载写法及使用
      • 4.2.3 默认赋值运算符重载
    • 4.3 日期类的实现
      • 4.3.1 创建类和函数声明
      • 4.3.2 全缺省的构造函数
      • 4.3.3 获取某年某月的天数
      • 4.3.4 析构、拷贝构造和赋值运算符重载
      • 4.3.5 日期+=天数
      • 4.3.6 日期-=天数
      • 4.3.7 日期+天数和日期-天数
      • 4.3.8 前置++和后置++
      • 4.3.9 前置--和后置--
      • 4.3.10 !=、>=、<、<=运算符重载
      • 4.3.11 日期-日期 返回天数
    • 4.4 日期类的全部代码
      • 4.4.1 Date.h
      • 4.4.2 Date.cpp
      • 4.4.3 test.cpp
  • 五、const成员函数
    • 5.1 const成员函数概念
    • 5.2 const成员函数写法
    • 5.3 关于const的权限问题
  • 六、取地址及const取地址操作符重载

前言:

类有6个默认成员函数,简单介绍下进入正题,分别是:

  • 构造函数——初始化
  • 析构函数——清理
  • 拷贝构造函数——使用同类对象初始化创建对象
  • 赋值运算符重载——一个对象赋值给另一个对象
  • const成员函数——修饰this指针
  • 取地址重载——获取对象地址

默认的意思是我们自己不写编译器自动生成,当然我们也可以自己写。所以,分析默认成员函数主要就围绕两个点:
1、写,应该怎样实现;
2、不写,是否符合要求。

一、构造函数

1.1 构造函数概念

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

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(2023, 10, 29);
	d1.Print();
	return 0;
}

运行结果:
在这里插入图片描述

1.2 为什么有构造函数

以前写C语言的时候,总要有个先初始化的函数,如果忘记写了,就会导致程序运行失败。虽然不要忘记写初始化是非常要注意的事情,但是仍然还是有失误的时候。所以呢,C++的创始人引入构造函数这个概念,在对象创建时,把要初始化的信息设置进去,同时也比以前更加方便。

1.3 构造函数的写法及使用

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象
构造函数有以下基本的特征:

1.函数名与类名相同
2.无返回值
3.对象实例化时编译器自动调用对应的构造函数
4.可以重载

构造函数支持重载让它有多种写法,概括一下就是有带参和无参的情况

1️⃣没有带参数:

Date()
	{
		_year = 4;
		_month = 5;
		_day = 6;
	}
	/
	Date d1;

注意:没有参数时d1后面是不能带括号的,如果带括号就变成声明函数了。

2️⃣有带参数:

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	/
	Date d1(2023, 10, 29);

构造函数参数可以是缺省参数

	Date(int year = 2020, int month = 9, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	/
	Date d1;

在这里插入图片描述

1.4 默认构造函数

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

1️⃣我们将构造函数注释掉:

/*Date(int year = 2020, int month = 9, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/

运行结果:
在这里插入图片描述
为什么会是随机值呢?这个问题后面会解决。但是可以确定在我们没写构造函数时,编译器会自动调用生成的构造函数。

2️⃣再写个栈类作对比:

class Stack
{
public:
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	return 0;
}

用构造函数初始化后值的结果:
在这里插入图片描述
数组_a是动态开辟的空间,没有放数据,所以是随机值。其他的被初始化后有相应的值。

栈类的构造函数也注释掉,看会发生什么:

	/*Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}*/

在这里插入图片描述
也是随机值,既然如此,那么我们不写构造函数时编译器自动生成的默认构造函数好像没什么用,其实不是这样的。

3️⃣再创建一个类,两个栈实现队列:

class Stack
{
public:
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
class Queue
{
	Stack st1;
	Stack st2;
	int size;
};
int main()
{
	Queue q1;
	return 0;
}

在这里插入图片描述
队列类没有写它的构造函数,为什么_top和_capacity不是随机值呢?因为内置类型的成员不做处理,自定义类型的成员会去调用它的构造函数。 这里就可以解释前面的日期类不写构造函数为什么是随机值了,因为日期类的成员变量都是内置类型,所以编译器不做处理,就为随机值。队列里有两个成员是自定义类型,会去调用自定义类型的成员的构造函数,如果自定义类型的成员我们也没有写,就是随机值。

内置类型:int float double …
自定义类型:struct class …

如果类里面都是内置类型的成员,又想要默认的构造函数,该怎么办?
C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year = 2023;
	int _month = 1;
	int _day = 1;
};
int main()
{
	Date d1;
	d1.Print();
	return 0;
}

在这里插入图片描述

1.5 哪些可为默认构造函数

1.没有传参的:

	Date()
	{
		_year = 1;
		_month = 2;
		_day = 3;
	}

2.全缺省的:

	Date(int year = 2020, int month = 9, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

3.自己不写,编译器自动生成的

以上这些都可以叫做默认构造函数,总结一下就是没有传参的为默认构造函数。

注意:写的时候只能出现其中一个,重复出现会报错

二、析构函数

2.1 析构函数概念

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

class Date
{
public:
	//构造函数
	Date(int year = 2020, int month = 9, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
		_year = 0;
		_month = 0;
		_day = 0;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Print();
	return 0;
}

在这里插入图片描述
打印~Date()就说明调用了析构函数。

2.2 为什么有析构函数

与构造函数的理由相反,前面的是防止忘记初始化,这里是防止忘记清理。其实清理对内置类型不作处理的(后面还会谈到),所以日期类不写析构函数让编译器自动生成就可以了。但是如果我们有申请空间,比如malloc,它不会随着程序结束而结束,要我们自己主动释放才行,否则会内存泄漏,所以这种情况必须自己写析构函数。

2.3析构函数的写法及使用

先了解下析构函数的几个特点:

1.析构函数名是在类名前加上字符 ~
2.无参数无返回值类型
3.一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
4.对象生命周期结束时,C++编译系统系统自动调用析构函数

因为析构函数不支持重载,所以写法只有一种:(日期类)

	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}

再来看看栈类:写法与以前的销毁函数一样

class Stack
{
public:
	//构造函数
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	//析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	return 0;
}

在这里插入图片描述
打印出这个结果说明调用了析构函数

2.4 默认析构函数

当我们不写时,编译器自动调用生成的析构函数。对内置类型不处理,对自定义类型调用它的析构函数

class Stack
{
public:
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
class Queue
{
	Stack st1;
	Stack st2;
	int size;
};
int main()
{
	Queue q1;
	return 0;
}

运行结果:
在这里插入图片描述
一个是st1的,另一个是st2的

注意:如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

三、拷贝构造函数

3.1 拷贝构造函数概念

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

	//拷贝构造函数
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

3.2 为什么有拷贝构造函数

当我们在调用一个函数传参时,采用传值调用(可以用引用,但是这里我们是要拷贝参数,毕竟是在拷贝构造函数的知识点里),形参是实参的一份拷贝,如果是内置类型,没有啥问题;如果传的是对象,会自动调用拷贝构造函数。

上面的例子是传值,即实参拷贝给形参,拷贝的是对象。
总之,不管哪种情形,只要是拷贝对象,就会自动调用拷贝构造函数

1️⃣日期类: 调用Func函数

class Date
{
public:
	Date(int year = 2020, int month = 9, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Func(Date d)
	{
		Print();
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Func(d1);
	return 0;
}

d1是实参,d是形参,d1拷贝给d,然后调用Func函数
运行结果:
在这里插入图片描述
没有什么问题。

2️⃣看看栈类会发生什么: 调用Func函数

class Stack
{
public:
	//构造函数
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	//析构函数
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	void Func(Stack st)
	{
		cout << "Func(Stack st1)" << endl;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	st1.Func(st1);
	return 0;
}

st1是实参,st是形参,st1拷贝给st,然后调用Func函数
运行结果:
在这里插入图片描述
编译器报错了,那么理由是什么呢?

如图:
在这里插入图片描述

st是st1的一份拷贝,st1的值赋给了st,但是两个_a的指向同一块空间,调用Func函数出了这个函数的作用域,自动调用析构函数,把这块空间清理了,然后程序运行结束,再次自动调用析构函数,这块空间相当于被清理了两次。前面第一次st的_a指向这块空间被释放了,出Func函数,st1的_a变成野指针了,程序结束再释放就导致程序崩溃

以上的写法都是浅拷贝,即值传递。日期类没有问题,但是栈类会出现前面报错的情况,解决栈类的问题,就要用深拷贝,在这种情况下必须要有拷贝构造函数

3.3 拷贝构造函数的写法及使用

先了解下拷贝构造函数的特点:

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

日期类:

	//拷贝构造函数
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

如果参数没有加引用,为什么会出现无穷递归?
先简单看一段代码(正确的):

class Date
{
public:
	Date(int year = 2023, int month = 10, int day = 29)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(Date& d)
	{
		_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;
}

在这里插入图片描述
如果拷贝构造函数变成这样:

	Date(Date d)

我们用图来分析:
在这里插入图片描述
所以参数必须是类类型对象的引用,后面的栈类的也一样。前面有Func函数的例子只不过多调用了一次Func函数,其他不变。
画图演示一下:
在这里插入图片描述

栈类:

	//拷贝构造函数——深拷贝
	Stack(Stack& st)
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, st._a, sizeof(int) * st._capacity);
		_top = st._top;
		_capacity = st._capacity;
	}

深拷贝不仅是拷贝值,还开辟了另一块空间,这样st和st1的_a的地址就不一样了,说明它们不是指向同一块空间。
验证一下:
在这里插入图片描述
运行结果:
在这里插入图片描述

3.4 默认拷贝构造函数

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

1️⃣日期类:没写拷贝构造(可写可不写)

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year = 2022;
	int _month = 10;
	int _day = 17;
};
int main()
{
	Date d1;
	Date d2(d1);
	d2.Print();
	return 0;
}

在这里插入图片描述

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

2️⃣栈类:必须写拷贝构造

class Stack
{
public:
	//构造函数
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	//析构函数
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	//拷贝构造函数
	Stack(Stack& st)
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, st._a, sizeof(int) * st._capacity);
		_top = st._top;
		_capacity = st._capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

在这里插入图片描述

对于内置类型,会进行值传递;对于的自定义类型,会调用它的拷贝构造。

3️⃣队列类:

class Stack
{
public:
	//构造函数
	Stack(int capacity = 3)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	//析构函数
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	//拷贝构造函数
	Stack(Stack& st)
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, st._a, sizeof(int) * st._capacity);
		_top = st._top;
		_capacity = st._capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
class Queue
{
	Stack st1;
	Stack st2;
	int size = 6;
};
int main()
{
	Queue q1;
	Queue q2(q1);
	return 0;
}

在这里插入图片描述

补充:
1.拷贝构造函数的参数的类型前面一般都要加上const,防止被修改(如果值有变化就不加)。
2.在一个函数内返回一个对象,这个对象是局部变量,出作用域就销毁了,所以返回的是它的拷贝,也会自动调用拷贝构造函数。
3.大多数情况都是可以传值(拷贝)的,但是使用引用效率更高,所以能使用引用就使用引用。

四、赋值运算符重载

4.1 运算符重载

常见的运算符有:

!= 、>、<、>=、<=、== …

我们可以对两个数进行比较:

int main()
{
	int x = 1;
	int y = 2;
	int z = x > y;
	cout << z << endl;// 0  
	return 0;
}

如果要两个对象进行比较:
在这里插入图片描述
说明两个对象不能直接进行比较,因为对象是自定义类型,这个类型里面有多个成员变量,不确定谁大谁小,所以不能直接比。

这种情况要用函数来区别对象内的成员的大小,才能具体分出哪个对象大哪个对象小。

class Date
{
public:
	Date(int year = 2023, int month = 10, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
//判断相等
bool Equal(const Date& x, const Date& y)
{
	//...
}
//判断是否大于
bool Greater(const Date& x, const Date& y)
{
	//...
}
int main()
{
	Date d1;
	Date d2(2022, 6, 6);
	d1.Print();
	d2.Print();

	//判断相等
	bool ret = Equal(d1, d2);
	//判断是否大于
	bool ret1 = Greater(d1, d2);
	cout << ret << endl;
	cout << ret1 << endl;
	return 0;
}

先来实现判断相等的函数:
这个比较简单,无非就是两个对象的年与年相等、月与月相等、天与天相等就好了,满足返回true,有一个条件不满足返回false。

bool Equal(const Date& x, const Date& y)
{
	return x._year == y._year
		&& x._month == y._month
		&& x._day == y._day;
}

实现判断是否大于的函数:
用if,else 语句,先判断年,年大返回true,否则不进这个判断;年相等比较月,月大返回true,否则不进这个判断;年和月相等比较天,天大返回true,否则不进这个判断。三个判断没有一个满足,返回false。

bool Greater(const Date& x, const Date& y)
{
	if (x._year > y._year)
	{
		return true;
	}
	else if (x._year == y._year && x._month > y._month)
	{
		return true;
	}
	else if (x._year == y._year 
		&& x._month == y._month 
		&& x._day > y._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

这里我们使用的函数名(Equal、Greater)能够一眼看出这个函数是干什么的,但是不是所有人都规范使用函数名,有的人用的函数名(compare1、compare2),怎么区分哪个是判断相等、哪个是判断是否大于呢?更严重的是使用中文拼音来作函数名的,非常不规范。

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

//判断相等
bool operator==(const Date& x, const Date& y)
{
	return x._year == y._year
		&& x._month == y._month
		&& x._day == y._day;
}
//判断是否大于
bool operator>(const Date& x, const Date& y)
{
	if (x._year > y._year)
	{
		return true;
	}
	else if (x._year == y._year && x._month > y._month)
	{
		return true;
	}
	else if (x._year == y._year 
		&& x._month == y._month 
		&& x._day > y._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}
/
	//判断相等
	bool ret = operator==(d1, d2);
	//判断是否大于
	bool ret1 = operator>(d1, d2);

运行结果:
在这里插入图片描述
这样写函数名就比以前好区分了,而且更规范。

其实还可以修改,让代码看起来更简洁:

	//判断相等
	bool ret = d1 == d2;
	//判断是否大于
	bool ret1 = d1 > d2;

或者是:

	//判断相等
	cout << (d1 == d2) << endl;
	//判断是否大于
	cout << (d1 > d2) << endl;

前面的的代码是有错误的:
1️⃣类成员变量是私有的,类外不能访问,所以把函数写在类里。

但是这时编译器有报错了:
在这里插入图片描述
2️⃣作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

正确代码:

class Date
{
public:
	Date(int year = 2023, int month = 10, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//判断相等
	bool operator==(const Date& y)
	{
		return _year == y._year
			&& _month == y._month
			&& _day == y._day;
	}
	//判断是否大于
	bool operator>(const Date& y)
	{
		if (_year > y._year)
		{
			return true;
		}
		else if (_year == y._year && _month > y._month)
		{
			return true;
		}
		else if (_year == y._year
			&& _month == y._month
			&& _day > y._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022, 6, 6);
	d1.Print();
	d2.Print();
	//判断相等
	bool ret = d1 == d2;
	//判断是否大于
	bool ret1 = d1 > d2;
	cout << ret << endl;
	cout << ret1 << endl;
	return 0;
}

运行结果:
在这里插入图片描述

4.2 赋值运算符重载

4.2.1 赋值运算符重载概念

有了前面的基础,我们现在能够知道运算符重载是具有特殊函数名的函数。前面学过大于运算符重载、相等运算符重载,那么,接下来学习的赋值运算符重载也有些类似,但又有所不同。

赋值运算符重载与前面的构造、析构和拷贝构造一样,是特殊的成员函数,自动调用。它可以实现两个原本已经存在的对象之间进行赋值操作。如果没有写,编译器会生成一个默认赋值运算符重载。

4.2.2 赋值运算符重载写法及使用

赋值运算符重载格式:
1.参数类型:const T&,传递引用可以提高传参效率
2.返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
3.检测是否自己给自己赋值
4.返回*this :要复合连续赋值的含义

   //  赋值运算符重载
	Date& operator=(const Date& d)
	{              
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

实现两个对象之间的赋值:

class Date
{
public:
	Date(int year = 2023, int month = 10, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022, 9, 1);
	d1.Print();
	d2.Print();
	printf("\n");
	d1 = d2;// d2赋给d1
	d1.Print();
	d2.Print();
	return 0;
}

运行结果:
在这里插入图片描述

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

4.2.3 默认赋值运算符重载

1️⃣用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

将赋值运算符重载注释掉:

	/*Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}*/

运行结果:
在这里插入图片描述

2️⃣内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Date
{
public:
	Date(int year = 2023, int month = 10, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator=(const Date& d)
	{
		cout << "Date& operator=(const Date& d)" << endl;
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
class Time
{
	Date d1;
	int size = 10;
};
int main()
{
	Time t1;
	Time t2;
	t2 = t1;
	return 0;
}

t2与t1是一样的,但是我们这里主要看是否有调用对应类的赋值运算符重载完成赋值。
运行结果:
在这里插入图片描述

补充:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

4.3 日期类的实现

4.3.1 创建类和函数声明

分为3个文件写,将声明、定义和测试区分。在头文件创建一个Date类,私有区域定义成员变量年、月、日;公有区域声明成员函数。

class Date
{
public:
	//打印
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//构造
	Date(int year = 2023, int month = 10, int day = 28);
	//析构
	//~Date();
	//获取某年某月的天数
	int Getmonthday(int year, int day);
	//拷贝构造
	//Date(Date& d);
	// 赋值运算符重载
	Date operator=(const Date& d);

	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day);
	// 日期-=天数
	Date& operator-=(int day);
	// 日期-天数
	Date operator-(int day);

	//前置++
	Date& operator++();
	//后置++
	Date operator++(int);
	//前置--
	Date& operator--();
	//后置--
	Date operator--(int);

	// >运算符重载
	bool operator>(const Date& d);
	// ==运算符重载
	bool operator==(const Date& d);
	// >=运算符重载
	bool operator>=(const Date& d);
	// <运算符重载
	bool operator<(const Date& d);
	// <=运算符重载
	bool operator<=(const Date& d);
	// !=运算符重载
	bool operator!=(const Date& d);

	// 日期-日期 返回天数
	int operator-(const Date& d);
private:
	int _year;
	int _month;
	int _day;
};

4.3.2 全缺省的构造函数

声明和定义分离,定义的函数名前要加类名和作用域限定符(后面的也一样),表示成员函数是这个类的。构造函数采用全缺省,要注意声明给缺省值,定义不能给,否则容易出错(两边给的缺省值一样还好说,不一样就问题大了,编译器不知道用哪边的)。

Date::Date(int year, int month, int day)
{
	if (year <= 0 || month >= 13 || month <= 0 || day <= 0
		|| day >= Getmonthday(year, month))
	{
		cout << "日期非法" << endl;
	}
	_year = year;
	_month = month;
	_day = day;
}

注意:年月日都是从1开始的

先加个条件判断日期是否合法,合法进行初始化,否则打印出日期非法,非法仍然可以打印出日期,打印出日期非法的作用是提醒编程者给的缺省值的不合理。
在这里插入图片描述
判断的括号里有Getmonthday函数,接下来分析

4.3.3 获取某年某月的天数

某年某月的天数是不固定的,所以要写个函数来获取该年该月的天数,方便后面的函数使用。

首先定义一个数组,数组内容是从1月到12月的天数,为了方便操作,数组的第一个元素给0,总之什么也不是,这样1月就从下标1开始(数组下标从0开始的)。定义一个变量叫day,等于数组下标,下标就是月份。然后判断平年、闰年,是闰年day+1,因为前面是数组的二月天数初始化为28;不是闰年就不进这个判断。返回day,获取某年某月的天数。

int Date::Getmonthday(int year, int month)
{
	static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = arr[month];
	if (month == 2 && ((year % 100 != 0 && year % 4 == 0) || year % 400 == 0))
	{
		day += 1;
	}
	return day;
}

4.3.4 析构、拷贝构造和赋值运算符重载

因为日期类的成员变量都是内置类型,所以析构、拷贝构造和赋值运算符重载我们可以不写,让编译器默认生成。

//析构
//Date::~Date()
//{
//	_year = 0;
//	_month = 0;
//	_day = 0;
//}

//拷贝构造
//Date::Date(const Date& d)
//{
//	_year = d._year;
//	_month = d._month;
//	_day = d._day;
//}

// 赋值运算符重载
//Date Date::operator=(const Date& d)
//{
//	if (this != &d)
//	{
//		_year = d._year;
//		_month = d._month;
//		_day = d._day;
//	}
//	return *this;
//}

4.3.5 日期+=天数

该年该月的日期加上天数,如果比该年该月的天数大,进入循环,先减去该年该月的天数,月再加1,里面补充个判断,如果月等于13,年加1,月更新到1月;天数减到在该年该月的范围内,跳出循环,返回日期。

Date& Date::operator+=(int day)
{
	_day += day;
	while (_day > Getmonthday(_year, _month))
	{
		_day -= Getmonthday(_year, _month);//
		_month++;//
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

4.3.6 日期-=天数

该年该月的日期减去天数,如果天数小于等于0,进入循环。先月减1,再加上该年该月的天数,在两者之间加判断,如果月等于0,年减1,月更新到12月;天数加到在该年该月的范围内,跳出循环,返回日期。

Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += Getmonthday(_year, _month);//
	}
	return *this;
}

4.3.7 日期+天数和日期-天数

复用前面的函数即可

Date Date::operator+(int day)
{
	Date tmp(*this);//自动调用拷贝构造
	tmp += day;//复用 日期+=天数
	return tmp;//返回的是tmp的拷贝
}
Date Date::operator-(int day)
{
	Date tmp(*this);//自动调用拷贝构造
	tmp -= day;//复用 日期-=天数
	return tmp;//返回的是tmp的拷贝
}

4.3.8 前置++和后置++

前置++是先加1,后使用。虽然++在操作数的前面,但是在运算符重载的规则里它的++还是在operator关键字的后面。

Date& Date::operator++()
{
	*this = *this + 1;
	return *this;
}

后置++是先使用后加1,返回的值是还没加1的值。

Date Date::operator++(int)
{
	Date tmp(*this);
	*this = *this + 1;
	return tmp;
}

4.3.9 前置–和后置–

与前面同:

//前置--
Date& Date::operator--()
{
	*this = *this - 1;
	return *this;
}
//后置--
Date Date::operator--(int)
{
	Date tmp(*this);
	*this = *this - 1;
	return tmp;
}

4.3.10 !=、>=、<、<=运算符重载

前面学习过>和= = 的运算符重载,接下来的就是> 和 ==的运算符重载的复用,使用逻辑反操作运和逻辑与两个运算符即可快速解决。

// >=运算符重载
bool Date::operator>=(const Date& d)
{
	return *this > d && *this == d;
}

// <运算符重载
bool Date::operator<(const Date& d)
{
	return !(*this > d && *this == d);
}

// <=运算符重载
bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}

// !=运算符重载
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

4.3.11 日期-日期 返回天数

刚开始不确定哪个日期比较大,先假设一个日期是大的,另一个是小的(如果判断错误就互换数据)。用一个变量统计天数,设置一个循环,小的日期不等于大的日期,计数++,直到相等跳出循环,返回天数。

int Date::operator-(const Date& d)
{
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int count = 0;
	while (max != min)
	{
		min++;//复用前面的函数
		count++;
	}
	return count * flag;
}

4.4 日期类的全部代码

4.4.1 Date.h

#include <iostream>
using namespace std;
class Date
{
public:
	//打印
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//构造
	Date(int year = 2023, int month = 10, int day = 30);
	//析构
	//~Date();
	//获取某年某月的天数
	int Getmonthday(int year, int day);
	//拷贝构造
	//Date(Date& d);
	// 赋值运算符重载
	//Date operator=(const Date& d);

	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day);
	// 日期-=天数
	Date& operator-=(int day);
	// 日期-天数
	Date operator-(int day);

	//前置++
	Date& operator++();
	//后置++
	Date operator++(int);
	//前置--
	Date& operator--();
	//后置--
	Date operator--(int);

	// >运算符重载
	bool operator>(const Date& d);
	// ==运算符重载
	bool operator==(const Date& d);
	// >=运算符重载
	bool operator>=(const Date& d);
	// <运算符重载
	bool operator<(const Date& d);
	// <=运算符重载
	bool operator<=(const Date& d);
	// !=运算符重载
	bool operator!=(const Date& d);

	// 日期-日期 返回天数
	int operator-(const Date& d);
private:
	int _year;
	int _month;
	int _day;
};

4.4.2 Date.cpp

#include "Date.h"

// 获取某年某月的天数
int Date::Getmonthday(int year, int month)
{
	static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = arr[month];
	if (month == 2 && ((year % 100 != 0 && year % 4 == 0) || year % 400 == 0))
	{
		day += 1;
	}
	return day;
}

//构造
Date::Date(int year, int month, int day)
{
	if (year <= 0 || month >= 13 || month <= 0 || day <= 0
		|| day >= Getmonthday(year, month))
	{
		cout << "日期非法" << endl;
	}
	_year = year;
	_month = month;
	_day = day;
}

//析构
//Date::~Date()
//{
//	_year = 0;
//	_month = 0;
//	_day = 0;
//}

//拷贝构造
//Date::Date(const Date& d)
//{
//	_year = d._year;
//	_month = d._month;
//	_day = d._day;
//}

// 赋值运算符重载
//Date Date::operator=(const Date& d)
//{
//	if (this != &d)
//	{
//		_year = d._year;
//		_month = d._month;
//		_day = d._day;
//	}
//	return *this;
//}

// 日期+=天数
Date& Date::operator+=(int day)
{
	_day += day;
	while (_day > Getmonthday(_year, _month))
	{
		_day -= Getmonthday(_year, _month);//
		_month++;//
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

// 日期+天数
Date Date::operator+(int day)
{
	Date tmp(*this);//自动调用拷贝构造
	tmp += day;//复用 日期+=天数
	return tmp;//返回的是tmp的拷贝
}

// 日期-=天数
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += Getmonthday(_year, _month);//
	}
	return *this;
}

// 日期-天数
Date Date::operator-(int day)
{
	Date tmp(*this);//自动调用拷贝构造
	tmp -= day;//复用 日期-=天数
	return tmp;//返回的是tmp的拷贝
}

//前置++
Date& Date::operator++()
{
	*this = *this + 1;
	return *this;
}

//后置++
Date Date::operator++(int)
{
	Date tmp(*this);
	*this = *this + 1;
	return tmp;
}

//前置--
Date& Date::operator--()
{
	*this = *this - 1;
	return *this;
}

//后置--
Date Date::operator--(int)
{
	Date tmp(*this);
	*this = *this - 1;
	return tmp;
}

// >运算符重载
bool Date::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;
}

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

// >=运算符重载
bool Date::operator>=(const Date& d)
{
	return *this > d && *this == d;
}

// <运算符重载
bool Date::operator<(const Date& d)
{
	return !(*this > d && *this == d);
}

// <=运算符重载
bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}

// !=运算符重载
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int count = 0;
	while (max != min)
	{
		min++;//复用前面的函数
		count++;
	}
	return count * flag;
}

4.4.3 test.cpp

#include "Date.h"
int main()
{
	Date d1;
	d1.Print();
	
	Date d2(d1);
	d2.Print();

	Date d3(2020, 2, 3);
	d1 = d3;
	d1.Print();

	d1 += 100;
	d1.Print();

	Date ret = d2 + 100;
	d2.Print();
	ret.Print();

	d2 -= 100;
	d2.Print();

	Date ret1 = d3 - 100;
	d3.Print();
	ret1.Print();

	d3 = d3 + 1;
	d3.Print();

	Date ret2 = d3 + 1;
	ret2.Print();

	d2 = d2 - 1;
	d2.Print();
	printf("\n");
	d1.Print();
	d2.Print();
	bool ret3 = d1 > d2;
	cout << ret3 << endl;
	bool ret4 = d1 == d2;
	cout << ret4 << endl;
	bool ret5 = d1 >= d2;
	cout << ret5 << endl;
	bool ret6 = d1 < d2;
	cout << ret6 << endl;
	bool ret7 = d1 <= d2;
	cout << ret7 << endl;
	bool ret8 = d1 != d2;
	cout << ret8 << endl;
	
	int count = d1 - d2;
	cout << count << endl;
	return 0;
}

运行结果:
在这里插入图片描述

五、const成员函数

5.1 const成员函数概念

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

5.2 const成员函数写法

先来看代码:

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

就是在函数参数的后面加个const,是不是看起来有点奇怪,但是这个是语法规定的,只能这样写。

5.3 关于const的权限问题

主要有以下几个问题:
1.const对象可以调用非const成员函数吗?
2.非const对象可以调用const成员函数吗?
3.const成员函数内可以调用其它的非const成员函数吗?
4.非const成员函数内可以调用其它的const成员函数吗?

先来一段代码:

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

	/*void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}*/
	
	void Func() const
	{
		//Print();
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 11, 3);
	
	d1.Func();
	return 0;
}

这个代码没问题

我们来对比下对象有const和没const的情况分别调用的函数是不是const成员函数:
在这里插入图片描述
成员函数调用其他成员函数的对比:
在这里插入图片描述

补充:
通常情况下,如果该函数里面没有修改成员变量,都可以是const成员函数;反之就不能是const成员函数。
如果是全局函数,不能使用const修饰,因为它不是类里面的成员函数,所以没有this指针(const修饰的是this指针)

六、取地址及const取地址操作符重载

最后两个默认成员函数比较简单,一般不用自己定义 ,编译器默认会生成。

    // 可写可不写
	Date* operator&()
	{
		return this;
	}
	// 可写可不写
	const Date* operator&()const
	{
		return this;
	}
	
	cout << &d1 << endl;

运行结果:
在这里插入图片描述

一般情况不需要写,极少情况下要让别人获取到指定的内容才需要自己写。

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

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

相关文章

制造行业数字化运维破局之道

项目背景 某大型汽车制造集团&#xff0c;致力于通过数字化、智能化运营手段为用户提升提供高品质的汽车产品和服务。IT部门不仅为内外部持续提供服务&#xff0c;同时为业务运营与核心系统运行提供重要支撑。数字化运维作为数字化转型的核心基础&#xff0c;不但要保障数据安…

CSDN规则详解(一)

文章目录 前言CSDN博客用户准则总则博客注册博客行为规则被投诉侵权用户处理规则附则 博客积分规则博客等级博客VIP文章说明后记 前言 CSDN是一个专业的技术社区&#xff0c;不仅可以分享自己的技术经验&#xff0c;还可以向其他行业专业人士学习。在CSDN上写出优秀的博客可以…

管理如何实现制度流程化 流程表单化 表单信息化 信息标准化?

业务化、流程化、信息化、数字化、自动化、智能化&#xff0c;是企业业务管理发展路径的六个必经阶段 制度业务化&#xff0c;业务表单化&#xff0c;表单流程化、流程信息化、信息标准化、标准制度华。 制度流程化、流程表单化、表单信息化、信息标准化、标准制度华。 管理…

Linux软件安装包管理器yum

Linux软件安装 Linux软件安装的本质 ​ 对于安装软件最基本的理解就是把可执行程序拷贝到指定路径下&#xff0c;我们知道直接输入指令就可以实现想要的功能&#xff0c;这些指令本质上都是放在指定路径下的可执行文件&#xff0c;如果我们把写好的程序编译后的可执行文件放到…

优维低代码实践:打包发布

导语 优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。…

MES系统生产看板

在现代制造业中&#xff0c;生产看板是一种重要的工具&#xff0c;用于实时监控和管理生产过程。而随着制造执行系统&#xff08;MES&#xff09;的发展&#xff0c;生产看板已经得到了数字化的升级和优化。 一、MES系统生产看板的概述 MES系统生产看板是制造执行系统在生产现…

客服发送一条消息背后的技术和思考

一、引言 在企业客服场景中&#xff0c;客服发送一条消息的背后&#xff0c;需要考虑网络通信、前端展示、后端存储以及安全性等多个方面的技术支持&#xff0c;单从前端层面来说&#xff0c;就需要考虑到消息的显示、状态更新、稳定传输以及极限操作消息不卡顿等场景&#xf…

Iceberg 基础知识与基础使用

1 Iceber简介 1.1 概述 为了解决数据存储和计算引擎之间的适配的问题&#xff0c;Netflix开发了Iceberg&#xff0c;2018年11月16日进入Apache孵化器&#xff0c;2020 年5月19日从孵化器毕业&#xff0c;成为Apache的顶级项目。 Iceberg是一个面向海量数据分析场景的开放表格…

Adobe After Effects 2024(Ae2024)在新版本中的升级有哪些?

After Effects 2024是Adobe公司推出的一款视频处理软件&#xff0c;它适用于从事设计和视频特技的机构&#xff0c;包括电视台、动画制作公司、个人后期制作工作室以及多媒体工作室。通过After Effects&#xff0c;用户可以高效且精确地创建无数种引人注目的动态图形和震撼人心…

新零售实用小技巧,大神都在用!

自动售货机&#xff0c;作为一种融合了数字技术和零售业的创新&#xff0c;正迅速改变着我们的购物习惯和零售商的经营方式。 这一技术趋势不仅为零售业带来了新的商机&#xff0c;也为消费者提供了更为便捷和个性化的购物体验。自动售货机不再仅仅是传统的商品分发设备&#x…

教师减负神器

在传统的成绩管理模式中&#xff0c;教师需要手动输入、整理、分析成绩数据&#xff0c;工作量大且繁琐。这不仅耗费了教师大量的时间和精力&#xff0c;还容易出现错误。为了解决这个问题&#xff0c;我们可以通过各种代码和Excel来实现学生自助查询成绩的功能。 一、建立成绩…

深度学习之基于Tensorflow卷积神经网络学生课堂坐姿姿势识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Tensorflow的卷积神经网络学生课堂坐姿姿势识别系统介绍 Tensorflow是一个流行的开源机器学习框架&#xff0c…

【深度学习项目从下载到运行】

本文只是介绍一个大致的流程&#xff0c;简单的介绍一个深度学习项目整体的一个从下载到运行的框架让初学者入门。 实际在运行的过程中可能会遇到各种各样的问题。 目录 代码下载python项目各个文件夹的解释一个深度学习项目要包含的各个模块配置环境命令行运行项目且看项目的参…

基于php+thinkphp+vue的学生公寓管理系统-宿舍管理-寝室管理系统

运行环境 开发语言&#xff1a;PHP 数据库:MYSQL数据库 应用服务:apache服务器 使用框架:ThinkPHPvue 开发工具:VScode/Dreamweaver/PhpStorm等均可 项目简介 本系统结合计算机系统的结构、概念、模型、原理、方法&#xff0c;在计算机各种优势的情况下&#xff0c;采用PHP语…

多模态论文阅读之VLMo

VLMo泛读 TitleMotivationContributionModelExpertimentsSummary Title VLMo:Unified Vision_Langugae Pre-Training with Mixture-of-Modality-Experts Motivation CLIP和ALIGN都采用dual-encoder的方式分别编码图像和文本&#xff0c;模态之间的交互采用cosine similarity…

AERMOD模型在大气环境影响评价中的实践技术应用

随着我国经济快速发展&#xff0c;我国面临着日益严重的大气污染问题。近年来&#xff0c;严重的大气污染问题已经明显影响国计民生&#xff0c;引起政府、学界和人们越来越多的关注。大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果&#xff0c;同时气象因…

ROS学习笔记(5):rviz和rosbag数据记录、回放

1.rviz 1.前提 rviz-三维可视化平台可以满足ROS针对机器人的可视化需求。在Gazebo那里也可以看到rviz可以让模型显示在rviz上。 2.rviz的安装与运行 1.rviz的安装 sudo apt-get install ros-melodic-rviz 2.rviz的运行 roscore rviz/rosrun rviz rviz 3.rviz界面 1.视图区…

历“九“弥坚,怿星战略转型开新篇

10月是收获的季节&#xff0c;也迎来了怿星9岁生日。1024&#xff0c;程序员节&#xff0c;作为一家技术人员占比超过75%的高新技术企业&#xff0c;怿星9周年庆典活动也在这一天如期而至。 我们坚定地走在正确的道路上 近两年&#xff0c;多重因素叠加导致行业动荡也带来了资…

喝酒聚会摇色子小程序源码系统+石头剪刀布+大转盘 带完整的部署教程

来咯来咯&#xff0c;大家都知道摇色子是一种古老而受欢迎的饮酒游戏。在当代年轻人的聚会中&#xff0c;常常都使用摇骰子这种方法来喝酒的。今天罗峰要给大家介绍是一款非常受欢迎的小程序源码系统喝酒聚会摇色子小程序源码系统&#xff0c;还有石头剪刀布&#xff0c;大转盘…

公司防泄密软件都有什么功能

公司防泄密软件都有什么功能 企业为什么要用到防泄密软件&#xff1f; 主要还是为了保护敏感的信息和数据&#xff0c;以减少数据泄漏和数据丢失的风险&#xff0c;并确保数据的机密性和完整性&#xff0c;然企业使用数据防泄密软件的原因有&#xff1a;保护敏感数据、合规性…