当前位置: 首页 > news >正文

【C++之类和对象篇002】

C++学习笔记---005

  • C++知识类和对象篇
    • 1、类的6个默认成员函数
    • 2、构造函数
      • 2.1、构造函数的特性
      • 2.2、内置类型和自定义类型
      • 2.3、什么是默认构造函数?
    • 3、析构函数
      • 3.1、什么是析构函数?
      • 3.2、析构函数的特性
      • 3.3、析构函数的释放顺序
    • 4、拷贝构造函数
      • 4.1、什么是拷贝构造函数?
      • 4.2、拷贝构造函数的特性
      • 4.3、拷贝构造函数典型调用场景
      • 4.4、理解传值传参和传引用传参
      • 4.5、const正确的适用
      • 4.6、拷贝构造的默认处理
      • 4.7、拷贝构造浅拷贝的无脑拷贝问题
    • 5、操作符重载,关键字operator
      • 5.1、关键字operator
      • 5.2、私有限定符情况
      • 5.3、了解‘.*’操作符
    • 6、赋值运算符重载
      • 6.1、 赋值运算符重载
      • 6.2、 拷贝构造函数与赋值运算符重载的区别
    • 7、类的6个默认成员函数总结

C++知识类和对象篇

前言:
前面篇章学习了C++对于C语言的语法优化,接下来继续学习,C++的类和对象中类的6个默认成员函数的知识。
/知识点汇总/

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

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

class A{ };

1.初始化和清理
初始化:构造函数主要完成初始化工作;
清理:析构函数主要完成释放/清理工作。
2.拷贝和复制
拷贝:拷贝函数使用同类对象初始化创建对象;
赋值重载:主要把一个对象赋值给另一个对象。
3.取地址和重载(相对于前4个做个了解)
主要是普通对象和const对象取地址,这两个很少会自己实现(由编译器实现)。

2、构造函数

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

2.1、构造函数的特性

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

其特征如下:

  1. 函数名与类名相同。
  2. 无返回值。(不是void,直接不用写)
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
#include <iostream>
using namespace std;class Date
{
public://构造函数,不写构造函数,编译器默认对于内置类型不执行操作,为随机值Date(){_year = 1;_month = 1;_day = 1;}//构造函数支持函数重载,支持缺省参数Date(int year, int month, int day){_year = year;_month = month;_day = day;}//构造函数,支持缺省参数,语法上可以与无参的构造函数重载,但是编译会有歧义,所以可以重载但有歧义。//编译报错显示:对重载函数调用不明确。//Date(int year = 1, int month = 2, int day = 3)//{//	_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,1,27);d2.Print();return 0;
}

2.2、内置类型和自定义类型

默认成员函数之一,构造函数,我们不写编译器就会生成一个,它并不会对内置类型执行什么操作(由编译器不同也有不同),会对自定义类型初始化0操作。
C++分为内置类型/基本类型,即语言自身定义的类型;int/char/doubl…还有自定义类型,即struct/class/enum…
默认生成的构造函数,对于内置类型不做处理,自定义类型会去调用他的默认构造。

#include <iostream>
using namespace std;
class A
{
public://构造函数,对自定义类型的默认处理A(){cout << "print_A()" << endl;_a = 0;cout << _a << endl;_a = 2;}
private:int _a;
};class Date
{
public://构造函数,不写构造函数随机值Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}
private://这里是声明,不是初始化,给的是缺省值int _year = 1;int _month = 1;int _day;A _a;
};
int main()
{Date d1;d1.Print();return 0;
}

所以具体还是需要,分析一个类型成员是否需要初始化需求;
需要就写构造函数,不需要就交给编译器执行默认动作。
结论:一般情况下,都是自己写更适用。如果没写构造函数,编译器才会生成;否则,一旦写了编译器就不会写了。

#include <iostream>
using namespace std;
class stack
{
public:stack(){cout << "Stack_Init()..." << endl;}
};
class MyQueue
{
private:stack str1;stack str2;
};
int main()
{//构造函数适用于自定义类型MyQueue q;return 0;
}

2.3、什么是默认构造函数?

默认的构造函数,不完全等于编译器生成的构造函数,还包括无参的和全缺省的构造函数。
一般构造函数只需要写一个,否则存在编译歧义。

所以什么是默认构造函数?
答:1.无参构造函数 2.全缺省构造函数 3.没写构造而由编译器默认生成的构造函数

简述:不需要传参就可以调用的构造函数,都可以叫默认构造函数。

使用构造函数通常满足三个条件:
1.条件1,因为我们写了与类同名且无返回类型的构造函数,所以编译器就不会写了。
2.条件2,但是我们自己写的额构造函数不成立,因为构造函数有2中,除了编译器生成的以外就是无参和全缺省,所以报错:没有合适的默认构造函数可用。
3.条件3,3种构造函数又只能写一种(推荐写全缺省),否则编译会与其它2种构造函数存在歧义。

#include <iostream>
using namespace std;
class Date
{
public://构造函数的条件不成立,所以报错Date(int year, int month, int day){_year = year;_month = month;_day = day;}//无参的构造函数Date(){_year = 2024;_month = 1;_day = 27;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}
private://这里是声明,不是初始化,给的是缺省值//注意:是因为C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,//即:内置类型成员变量在类中声明时可以给默认值。不等于初始化int _year = 1;int _month = 1;int _day;
};
int main()
{//构造函数的条件不成立,所以报错//1.条件1,因为我们写了与类同名且无返回类型的构造函数,所以编译器就不会写了。//2.条件2,但是我们自己写的额构造函数不成立,因为构造函数有2中,除了编译器生成的以外就是无参和全缺省,所以报错:没有合适的默认构造函数可用。//3.条件3,3种构造函数又只能写一种(推荐写全缺省),否则编译会与其它2种构造函数存在歧义。Date d1;d1.Print();//构造函数支持函数重载,直接对象传参Date d2(2024, 2, 11);d2.Print();return 0;
}

3、析构函数

主要完成的清理和释放资源(常用于malloc等开辟的空间释放)

3.1、什么是析构函数?

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

#include <iostream>
using namespace std;
class Date
{
public://无参的构造函数Date(){_year = 2024;_month = 1;_day = 27;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}//析构函数的格式,一般用户自定义类型,比如栈...~Date(){//验证编译器确实会在, 对象生命周期结束时默认调用执行cout << "~Date" << endl;}
private:int _year = 1;int _month = 1;int _day;
};
int main()
{Date d1;d1.Print();return 0;
}

3.2、析构函数的特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
#include <iostream>
#include <assert.h>
using namespace std;typedef int DataType;class Date
{
public://无参的构造函数Date(){_year = 2024;_month = 1;_day = 27;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}//析构函数的格式,一般用户自定义类型,比如栈...~Date(){//验证编译器确实会在, 对象生命周期结束时默认调用执行cout << "~Date" << endl;}
private:int _year;int _month;int _day;
};class Stack
{
public:Stack(int n = 4){//放个打印,表示默认执行了的cout << "Stack" << endl;_array = (DataType*)malloc(sizeof(DataType) * n);if (_array == nullptr){perror("malloc fail");return;}_size = 0;_capacity = n;}void PushBack(DataType data){//扩容if (_size == _capacity){int newcapacity = _capacity == 0 ? 4 : _capacity * 2;DataType* tmp = (DataType*)realloc(_array, sizeof(DataType) * newcapacity);if (tmp == nullptr){perror("realloc fail");return;}_array = tmp;_capacity = newcapacity;}_array[_size] = data;_size++;}DataType GetTop(){return _array[_size-1];}void PopBack(){_size--;}//DataType& GetPopVal()//{//	return _array[_size-1];//}bool Empty(){return _size == 0;}~Stack(){//放个打印,表示默认执行了的cout << "~Stack" << endl;if (_array){free(_array);_array = nullptr;_size = 0;_capacity = 0;}}
private:DataType* _array;int _size;int _capacity;
};int main()
{Date d1;d1.Print();//Stack s1(4);Stack s1;s1.PushBack(1);s1.PushBack(2);s1.PushBack(3);while (!s1.Empty()){cout << s1.GetTop() << endl;s1.PopBack();}return 0;
}

小结

1.可以发现,先打印的 ~Stack ,再打印的 ~Date,显然先创建的d1对象,后创建的s1对象;
说明,析构满足栈的特性。先进后出/后进先出。即:先创建的后析构。
2.如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
3.编译器默认生成的析构与构造函数类似;内置类型不做处理,自定义类型则调用它的写的析构(适合Queue,stack)

3.3、析构函数的释放顺序

析构函数的释放顺序:
局部对象(后定义的先析构)–》局部静态–》全局对象(后定义的先析构)

补充:虽然编译器对于构造函数和析构函数对自定义函数处理,但自定义类型的底层还是内置类型的嵌套。

#include <iostream>
using namespace std;class Date
{
public://全缺省的构造函数Date(int year){_year = year;}//析构函数~Date(){cout << "~Date()" << _year <<  endl;}
private:int _year;int _month;int _day;
};
Date d6(6);
static Date d7(7);void Test()
{Date d4(4);static Date d5(5);
}
int main()
{Date d1(1);Date d2(2);static Date d3(3);Test();return 0;
}

4、拷贝构造函数

4.1、什么是拷贝构造函数?

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

4.2、拷贝构造函数的特性

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
    注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
  4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。所以是根据实际的应用场景决定
    注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

4.3、拷贝构造函数典型调用场景

1.使用已存在对象创建新对象
2.函数参数类型为类类型对象
3.函数返回值类型为类类型对象

#include <iostream>
using namespace std;class Date
{
public://默认的成员函数,都别忘了隐式的this指针参数//全省参数构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数的基本格式//const目的理解为控制权限/权限保护Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{Date d1(2024, 1, 28);d1.Print();//拷贝构造Date d2(d1);d2.Print();return 0;
}

4.4、理解传值传参和传引用传参

C++规定,执行自定义类型都会调用拷贝构造,执行了拷贝构造之后再进入自定义类型执行。

#include <iostream>
using namespace std;class Date
{
public://默认的成员函数,都别忘了隐式的this指针参数//全省参数构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数的基本格式//const目的理解为控制权限/权限保护Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};
//传值传参
//由于C++规定的这个特性,就可能会导致造成“死递归”的循环去调用拷贝构造。
//所以通常会结合const修饰参数的权限/属性
void func1(Date d)
{}
//传引用传参,引用不会调用拷贝构造,它是别名直接就可以操作参数。
void func2(Date& d)
{}
int main()
{Date d1(2024, 1, 28);d1.Print();//拷贝构造Date d2(d1);d2.Print();//C++规定自定义类型都会先调用拷贝构造//调用拷贝构造之前会先传参,又因为是传值传参就会形成新的拷贝构造,依次类推的陷入死循环func1(d1);func2(d2);return 0;
}

4.5、const正确的适用

const正确的适用有利于保护数据/权限访问/参数检查

#include <iostream>
using namespace std;class Date
{
public://默认的成员函数,都别忘了隐式的this指针参数//全省参数构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数的基本格式//Date(Date& d)易错//const目的理解为控制权限/权限保护Date(const Date& d){//const保护参数是否写法的检查//d._year = this->_year;_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{Date d1(2024, 1, 28);d1.Print();//拷贝构造Date d2(d1);d2.Print();Date d3(d2);d3.Print();return 0;
}

4.6、拷贝构造的默认处理

拷贝构造会对自定义类型进行默认处理
1.若没有显式定义的拷贝构造函数,那么编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按照内置类型成员按照内存存储的额字节依次完成拷贝,这种叫作浅拷贝。或叫值拷贝。
2.另外对于自定义的类型的拷贝函数称为深拷贝/引用拷贝。 拷贝构造函数的参数只能有一个并且必须是类类型对象的引用,因为传值是死递归直接报错。

#include <iostream>
using namespace std;class Time
{
public://强制编译器生成构造函数,因为后面写了拷贝构造函数,所以需要自己写,//但这里只是为了验证拷贝构造函数的深拷贝,所以构造函数就没什么意义,//就使用强制默认构造defaultTime() = default;//拷贝构造函数Time(const Time& t){//打印验证cout << "Time()" << endl;_hour = t._hour;_minute = t._minute;_second = t._second;}void Print(){cout << _hour << " - " << _minute << " - " << _second << endl;}
private:int _hour = 1;int _minute = 1;int _second = 1;
};class Date
{
public:Date(int year = 1, 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;//}void Print(){cout << _year << " - " << _month << " - " << _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;//验证自定义类型Time _t;
};int main()
{Date d1(2024, 1, 28);d1.Print();//Time t1;//t1.Print();//拷贝构造Date d2(d1);d2.Print();Date d3(d2);d3.Print();return 0;
}

小结
若未显式定义,编译器会自动生成默认的拷贝构造,传值拷贝会调用拷贝构造函数,称为浅拷贝;
对自定义类型的成员则调用它自己的拷贝构造,称为深拷贝。
补充
为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

4.7、拷贝构造浅拷贝的无脑拷贝问题

那么既然编译器不管内置类型还是自定义类型,都会帮我们执行拷贝工作,那么还需要我们自己写?
答:当然,编译器不可能完成一些复杂的逻辑和操作,所以遇到栈等开辟额外空间的操作时,需要我们自己注意完善。

#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}//深拷贝处理//解决办法就是开辟另一块同样大小的空间进行相关的操作Stack(const Stack& s){DataType* tmp = (DataType*)malloc(s._capacity * sizeof(DataType));if (nullptr == tmp){perror("malloc申请空间失败");return;}//拷贝数据 -->sizememcpy(tmp, s._array, sizeof(DataType) * s._size);_array = tmp;_size = s._size;_capacity = s._capacity;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);//拷贝构造函数Stack s2(s1);return 0;
}

这段代码的核心问题在于,拷贝构造函数会连同数组的地址一同无脑的拷贝,所以在程旭执行结束执行free释放时,由于两个数组地址指向了同一块地址空间,那么释放掉其中一个指针,那么另外一个再去释放,就造成了同一块区域空间的重复释放的问题。

解决办法就是我们自己编写深拷贝,处理正确的数组指针,正确的释放。
小结:一般来说,浅拷贝不适用于malloc开辟空间这种情况。所以一般交给深拷贝处理。

5、操作符重载,关键字operator

5.1、关键字operator

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
操作符重载为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意
1.不能通过连接其他符号来创建新的操作符:比如operator@。
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变, 例如:内置的整型 + ,不能改变其含义作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
4.注意:‘.*’ , ‘::’ , ‘sizeof’ , ‘?:’ , ‘.’ 注意以上5个运算符不能重载。

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
//private:int _year;int _month;int _day;
};//bool DateEqual(const Date& x, const Date& y)
//运算符重载:operator + 运算符
bool operator==(const Date& x, const Date& y)
{return x._year == y._year && x._month == y._month && x._day == y._day;
}
//日期不能整体比较
//2024 1 28
//2024 1 27
//2024 2 26//bool DateLess(const Date& x, const Date& y)
//运算符重载:operator + 运算符
bool operator<(const Date& x, const Date& y)
{if (x._year < y._year){return true;}else if (x._year == y._year){if (x._month < y._month){return true;}else if (x._month == y._month){return x._day < y._day;}}//写完true,则其余情况falsereturn false;
}int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 26);//对象不能直接比较,不能strcmp//error:d1 < d2//error:strcmp(d1,d2)//cout << DateEqual(d1, d2) << endl;//cout << DateLess(d1, d2) << endl;//为了可读性和通识性,引入关键词operator//对运算符的需要重新控制//operator+运算符 作函数名cout << operator==(d1, d2) << endl;cout << operator<(d1, d2) << endl;//等价写法:但是注意,传参参数的和函数的参数顺序绑定的,不可任意交换参数顺序cout << (d1 ==  d2) << endl;//cout << operator==(d1, d2) << endl;cout << (d1 < d2) << endl;//cout << operator<(d1, d2) << endl;return 0;
}

重载的运算符通常是具备一定意义的运算符。
注意以上的访问操作是基于,类的成员变量处于pubilc状态的。
那么如何解决私有状态的正确访问呢?

5.2、私有限定符情况

1.写“回调函数” Getyear()/ Getmonth()/ Getday()
2.直接在类里写其相关函数即可

私有限定符情况,解决方法1:

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//解决方法1:int Getyear(){return _year;}int Getmonth(){return _month;}int Getday(){return _day;}
private:int _year;int _month;int _day;
};bool operator==(Date& x,  Date& y)
{return x.Getyear() == y.Getyear()&& x.Getmonth() == y.Getmonth()&& x.Getday() == y.Getday();
}bool operator<(Date& x, Date& y)
{if (x.Getyear() < y.Getyear()){return true;}else if (x.Getyear() == y.Getyear()){if (x.Getmonth() < y.Getmonth()){return true;}else if (x.Getmonth() == y.Getmonth()){return x.Getday() < y.Getday();}}return false;
}int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 26);cout << operator==(d1, d2) << endl;cout << operator<(d1, d2) << endl;//等价写法:但是注意,传参参数的和函数的参数顺序绑定的,不可任意交换参数顺序cout << (d1 == d2) << endl;//cout << operator==(d1, d2) << endl;cout << (d1 < d2) << endl;//cout << operator<(d1, d2) << endl;return 0;
}

私有限定符情况,解决方法2:

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//解决方法2:直接放进类中,会报错,运算符函数的参数太多。//因为包括了隐含的成员函数this指针。//所以去掉多于参数,并且放进类之后,本身也就只需要被比较的参数了//bool operator==(const Date& x, const Date& y)bool operator==(const Date& y){return _year == y._year&& _month == y._month&& _day == y._day;}//bool operator<(const Date& x, const Date& y)bool operator<(const Date& y){if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}//写完true,则其余情况falsereturn false;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 1, 28);Date d2(2024, 2, 26);//cout << operator==(d1, d2) << endl;//cout << operator<(d1, d2) << endl;cout << d1.operator==(d2) << endl;cout << d1.operator<(d2) << endl;cout << d1.operator==(d1) << endl;cout << d1.operator<(d1) << endl;cout << d2.operator==(d2) << endl;cout << d2.operator<(d2) << endl;cout << d2.operator==(d1) << endl;cout << d2.operator<(d1) << endl;//等价写法:但是注意,传参参数的和函数的参数顺序绑定的,不可任意交换参数顺序cout << (d1 == d2) << endl;//cout << operator==(d1, d2) << endl;cout << (d1 < d2) << endl;//cout << operator<(d1, d2) << endl;return 0;
}

5.3、了解‘.*’操作符

'.’ 等价于 '->'属于成员指针运算符(不支持重载)

#include <iostream>
using namespace std;class ob
{
public:void func(){cout << "void func()" << endl;}
};typedef void(ob::* pobfunc)();int main()
{pobfunc p = &ob::func;ob temp;//(*p)();//this(temp.*p)();return 0;
}

6、赋值运算符重载

6.1、 赋值运算符重载

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

6.2、 拷贝构造函数与赋值运算符重载的区别

1.赋值运算符只能重载成类的成员函数不能重载成全局函数;
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。 此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
2.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?
答:当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
即与拷贝构造函数类似,涉及malloc动态开辟的空间操作就是自己写,编译器会把数组地址无脑拷贝

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}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){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;}//不写赋值重载,编译器会自动生成,但是只能是类的成员函数,//因为当用户在类外自己实现一个全局的赋值运算符重载时,就和编译器在类中生成的默认赋值运算符重载冲突了。//d1 = d2//void operator=(const Date& d)//满足,赋值运算符的连续赋值,添加Date返回值类型。//传值和传引用,返回又有一定的区别,传值只是拷贝,传引用才能改变变量Date& operator=(const Date& d){//添加,if判断使其不会浪费空间资源去执行赋值操作。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(2024, 1, 28);Date d2(2024, 2, 26);//拷贝构造Date d3(d1);//拷贝构造,同类型中一个已存在对象进行初始化要创建的对象//d1 = d2;//已经存在的对象,一个拷贝赋值给另一个存在对象d1 = d2;d1.Print();d2.Print();//连续赋值int i, j, k;i = j = k = 10;//优先级cout << ((i = j) = 10) << endl;cout << (i = j = 10) << endl;//那么赋值重载的运算符能实现连续赋值操作吗?//当然能,解决没有找到void类型的右操作数的运算符。//修改返回值为类类型并加引用。//不写赋值重载,编译器会自动生成d1 = d2 = d3;//偶尔会测试/失误的自己会给自己赋值//d1 = d1;//添加,if判断使其不会浪费空间资源去执行赋值操作。return 0;
}

小结:

1.用户没有显示实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝; 赋值重载与拷贝构造函数类似的,赋值重载内置类型可由编译器自动赋值,自定义类型调用它的赋值重载赋值。
2.不写赋值重载,编译器会自动生成,但是只能是类的成员函数,因为当用户在类外自己实现一个全局的赋值运算符重载时,就和编译器在类中生成的默认赋值运算符重载冲突了。

7、类的6个默认成员函数总结

1.构造函数和析构函数可归为一组,内置类型不做处理,自定义类型调用对应的构造函数和析构函数。
2.拷贝构造函数和赋值重载可归为一组,内置类型拷贝或赋值(编译器无法干一些复杂的逻辑操作,比如malloc开辟的空间处理等),自定义类型调用对应的拷贝构造函数和赋值重载

相关文章:

【C++之类和对象篇002】

C学习笔记---005 C知识类和对象篇1、类的6个默认成员函数2、构造函数2.1、构造函数的特性2.2、内置类型和自定义类型2.3、什么是默认构造函数&#xff1f; 3、析构函数3.1、什么是析构函数&#xff1f;3.2、析构函数的特性3.3、析构函数的释放顺序 4、拷贝构造函数4.1、什么是拷…...

k8s学习(RKE+k8s+rancher2.x)成长系列之简配版环境搭建(三)

3.19.切换RKE用户&#xff0c;并做免密登录&#xff08;三台机器相互免密&#xff09; su rke cd~ ssh-keygen[rkemaster.ssh]$ssh-copy-id rkeslaver2 [rkemaster.ssh]$ssh-copy-id rkeslaver1 [rkemaster.ssh]$ssh-copy-id rkemaster3.20.搭建RKE集群 为了方便理解&#…...

基于SSM的疫情期间学生信息管理平台的设计与实现(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的疫情期间学生信息管理平台的设计与实现&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…...

LeetCode_20_简单_有效的括号

文章目录 1. 题目2. 思路及代码实现&#xff08;Python&#xff09;2.1 栈 1. 题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型…...

gRPC 备查

简介 HTTP/2 HTTP/2 的三个概念 架构 使用流程 gRPC 的接口类型 1.单一RPC 2.服务器流式RPC 3.客户端式流式RPC 4.双向流式RPC...

MySQL 基础知识(十)之 MySQL 架构

目录 1 MySQL 架构说明 2 连接层 3 核心业务层 3.1 查询缓存 3.2 解析器 3.3 优化器 3.4 执行器 4 存储引擎层 5 参考文档 1 MySQL 架构说明 下图是 MySQL 5.7 及其之前版本的逻辑架构示意图 MySQL 架构大致可分为以下三层&#xff1a; 连接层&#xff1a;负责跟客户…...

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--大模型、扩散模型

专属领域论文订阅 VX关注{晓理紫},每日更新论文,如感兴趣,请转发给有需要的同学,谢谢支持 如果你感觉对你有所帮助,请关注我,每日准时为你推送最新论文。 为了答谢各位网友的支持,从今日起免费为300名读者提供订阅主题论文服务,只需VX关注公号并回复{邮箱+论文主题}(如…...

Delphi v11 安卓权限申请

问题 Delphi 10.4 的安卓权限申请代码&#xff0c;在 Delphi 11 下面编译无法通过。 原因 原因是里面有几个变量类型的定义有所不同。 procedure TDmBLE.RequestPermissionsResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TAr…...

频谱仿真平台HTZ Communications为私有5G建设铺平道路

韩国的国家监管机构韩国通信委员会&#xff08;KCA&#xff09;计划在德思特频谱仿真平台HTZ Communications的支持下加快扩大无线电接入范围&#xff0c;提升全国电信服务的质量和效率。 韩国通信委员会&#xff08;KCA&#xff09;在韩国的监管环境中扮演着至关重要的角色&am…...

【高效开发工具系列】PyCharm使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

进程终止与进程等待

fork 函数 fork 函数是 Linux 中一个非常重要的函数&#xff0c;它的作用是从已存在的进程中创建一个新进程。这个新进程就是当前进程的子进程。 fork() 函数使用方法&#xff1a;它在头文件 #include <unistd.h> 中&#xff0c;函数原型为 pid_t fork(void); 用一个…...

MySQL 基础知识(六)之数据查询(二)

目录 6 数值型函数 7 字符串函数 8 流程控制函数 9 聚合函数 10 分组查询 (group by) 11 分组过滤 (having) 12 限定查询 (limit) 13 多表查询 13.1 连接条件关键词 (on、using) 13.2 连接算法 13.3 交叉连接 (cross join) 13.4 内连接 (inner join) 13.5 外连接 …...

蓝桥杯嵌入式STM32G431RBT6知识点(主观题部分)

目录 1 前置准备 1.1 Keil 1.1.1 编译器版本及微库 1.1.2 添加官方提供的LCD及I2C文件 1.2 CubeMX 1.2.1 时钟树 1.2.2 其他 1.2.3 明确CubeMX路径&#xff0c;放置芯片包 2 GPIO 2.1 实验1&#xff1a;LED1-LED8循环亮灭 ​编辑 2.2 实验2&#xff1a…...

ELAdmin 部署

后端部署 按需修改 application-prod.yml 例如验证码方式、登录状态到期时间等等。 修改完成后打好 Jar 包 执行完成后会生成最终可执行的 jar。JPA版本是 2.6&#xff0c;MyBatis 版本是 1.1。 启动命令 nohup java -jar eladmin-system-2.6.jar --spring.profiles.active…...

计算机功能简介:EC, NVMe, SCSI/ISCSI与块存储接口 RBD,NUMA

一 EC是指Embedded Controller 主要应用于移动计算机系统和嵌入式计算机系统中&#xff0c;为此类计算机提供系统管理功能。EC的主要功能是控制计算机主板上电时序、管理电池充电和放电&#xff0c;提供键盘矩阵接口、智能风扇接口、串口、GPIO、PS/2等常规IO功能&#xff0c;…...

linux上安装bluesky的步骤

1、设备上安装的操作系统如下&#xff1a; orangepiorangepi5b:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.2 LTS Release: 22.04 Codename: jammy 2、在用户家目录下创建一个目录miniconda3目录&a…...

视频监控需求八问:视频智能分析/视频汇聚平台EasyCVR有何特性?

最近TSINGSEE青犀视频在与业内伙伴进行项目合作的过程中&#xff0c;针对安防监控可视化视频管理系统EasyCVR视频融合平台在电信运营商项目中的应用&#xff0c;进行了多方面的项目需求沟通。今天我们就该项目沟通为案例&#xff0c;来具体了解一下用户关心度较高的关于视频智能…...

django rest framework 学习笔记2

注意&#xff1a;该文章部分摘抄之百度&#xff0c;仅当做学习笔记供小白使用&#xff0c;若侵权请联系删除&#xff01; 显示关联表的数据&#xff0c;本示例会显示所有的关联的数据信息 from rest_framework import serializers from .models import Student class StudentM…...

第四篇【传奇开心果系列】Python文本和语音相互转换库技术点案例示例:pyttsx3自动化脚本经典案例

传奇开心果短博文系列 系列短博文目录Python文本和语音相互转换库技术点案例示例系列 短博文目录前言一、雏形示例代码二、扩展思路介绍三、批量处理文本示例代码四、自定义语音设置示例代码五、结合其他库和API示例代码六、语音交互系统示例代码七、多语言支持示例代码八、添加…...

model.train()和model.eval()两种模式的原理

1. model.train() 在使用 pytorch 构建神经网络的时候&#xff0c;训练过程中会在程序上方添加一句model.train()&#xff0c;作用是 启用 batch normalization 和 dropout 。 如果模型中有BN层&#xff08;Batch Normalization&#xff09;和 Dropout &#xff0c;需要在 训练…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…...

[拓扑优化] 1.概述

常见的拓扑优化方法有&#xff1a;均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有&#xff1a;有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...