C++ :类 和 对象 ※重点※(三)

简介: C++ :类 和 对象 ※重点※(三)

运算符重载:

内置类型是直接支持比较和运算的,那我们的自定义类型可以随意的去运算比较嘛?是不是不能阿,所以就有了运算符重载(这样还可以提高可读性):


函数名:关键字operator后面接需要重载的运算符

参数:它是跟操作数一起变化的

返回值:运算符运算后的结果

注意:不能通过链接其他不是运算符的符号来创建一个新的操作符

有这五个运算符是不能重载的:(  ::  sizeof   ?:   .   .*  )

class stu
{
public:
    stu(const stu& d)
    {
        _year = d._year;
    _month = d._month;
    _day = d._day;
    }
  stu(int year = 2022,int month = 1,int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
    bool operator==(stu& d1,stu& d2);
private:
  int _year;
  int _month;
  int _day;
};
bool stu::operator==(stu& d1,stu& d2)
{
    return d1._year==d2._year && d1._month==d2._month && d1._day==d2._day;
}
int main()
{
  stu s1(2000,6,9);
    stu s2(2011,5,19);
    if(s1==s2)//这里也是可以这样写的,operator(s1,s2),但这样太别扭了,跟调用函数一样
    {
        cout<<"YES"<<endl;
    }
} 

但是我们上面这段代码是有问题的:

c3525845c08c453c94c70abddfbe4df5.png


这是因为我们在类里面定义,是会有一个this指针的,正确的定义是这样的:

class stu
{
public:
  stu(const stu& d)
  {
    _year = d._year;
    _month = d._month;
    _day = d._day;
  }
  stu(int year = 2022, int month = 1, int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
  bool operator==(stu& d1);
private:
  int _year;
  int _month;
  int _day;
};
bool stu::operator==(stu& d1)
{
  return d1._year == _year && d1._month == _month && d1._day == _day;
}
int main()
{
  stu s1(2000, 6, 9);
  stu s2(2011, 5, 19);
  if (s1 == s2)//这里也是可以这样写的,s1.operator(s2)
    {
    cout << "YES" << endl;
  }
}


我们的重载也是可以定义为全局的,只不过这样你必须保证你能拿到对象里的成员,可以定义为共有 或者 写个函数去获取(这个是java里面喜欢用的)、写成友元函数:

class stu
{
public:
  stu(const stu& d)
  {
    _year = d._year;
    _month = d._month;
    _day = d._day;
  }
  stu(int year = 2022, int month = 1, int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
//private:
  int _year;
  int _month;
  int _day;
};
bool operator==(stu& d1,stu& d2)
{
  return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{
  stu s1(2000, 6, 9);
  stu s2(2011, 5, 19);
  if (s1 == s2)//这里也是可以这样写的,operator(s1,s2),但这样太别扭了,跟调用函数一样
  {
    cout << "YES" << endl;
  }
}

假如我们又有全局也有类里面的,如果全都又它会调用谁呢?

注意这里如果要验证记得拿一个函数的参数加上const,不然编译会报错 :

447b9c707dd143659b8fe53e65e85342.png

:调用我们的全局的,我们的编译器会先去类里面找,如果没有再去全局里面找,像上面那种s1.operator(s2)这个是指定了去调用类里面的


现在再来实现一下比较大小吧,大家觉得实现一个 大于 的重载应该怎么写呢

    bool operator>(const stu& d) const
  {
    if ((_year > d._year) || (_year == d._year && _month > d._month)
      || (_year == d._year && _month == d._month && _day > d._day))
    {
      return true;
    }
    return false;
  }

这里函数的后面那个const 是为了给this修饰的(不会给其他形参修饰),因为我们的this是不能显示写在形参的位置的,所以有了这样的方法。这里也是因为不涉及值得改变所以才用const来修饰,十分严谨


还有一个小细节:我们在平常写的时候一个运算符可能会涉及一些和它相关的运算符,这些运算符如果写了,就可以直接用这些写好的,更加方便比如 >= 就是 > 和 = 。


接着来看一下赋值重载:

   stu& operator=(const stu& d)
  {
    if (this != &d)//防止自己给自己赋值
    {
      _year = d._year;
      _month = d._month;
      _day = d._day;
      return *this;
    }
  }

注意这里是有返回值的,我们在赋值完后,可能还会赋值一次,也就是连续赋值,我们这里也是返回的引用,避免了传值返回调用拷贝构造带来的消耗

赋值重载和拷贝构造是不一样的,拷贝构造是去创建一个为定义的对象,赋值重载是两个操作数都存在的赋值

六大成员函数里面是有我们的赋值重载的,那我们自己不写,让编译器自动生成,实现的操作又是什么样的呢?

其实赋值重载参考的是拷贝构造,对于内置类型会完成他的值拷贝 ,自定义类型会去调用它的赋值重载。


重载前置++后置++?

对于重载运算符还有几个要说的点:大家觉得我们的前置++ 和 后置++ 应该怎么写呢?

1. stu operator++()
2. stu operator++()

他们函数名相同 返回值也相同,是不是应该用重载来实现阿

1. stu operator++()
2. stu operator++(int i)

编译器规定带参数的是后置++ 不带的是前置   并且规定传参是一个整型,传参这个操作我们不用管这个是编译器来帮我们做的

    stu& operator++()
  {
    *this += 1;
    return *this;
  }
  stu operator++(int)
  {
    date tmp(*this);
    *this += 1;
    return tmp;
  }


后置 ++ 返回的是一个临时拷贝,所以不能够返回引用!!!

注意:我们这里的参数 int 是为了让编译器传参,区分是 前置 还是 后置,所以这里是不能是加缺省值的,加了两个函数都可以不传参调用,这样就报错了,这里没有给形参,也是因为我这里只是要告诉编译器这里要传参,不在乎传的值,方便编译器判断。我不写形参的意思就是:这个值不重要,我可以选择不接收或者接收了但我不需要


重载流插入流提取?

流插入:cout <<  流提取:  cin >>

我们的 cin 和 cout都是存在头文件<iostream>,他们分别是两个流的对象,istream->cin,ostream->cout。像我们的内置类型都是已经重载好了的

void operator<<(ostream& out)//延续上面的代码,成员变量依旧是这几个日期
{
  out << _year << '-' << _month << '-' << _day << endl;
}

如果我这个重载写在类里面大家觉得写得对嘛?

  当然肯定是不对的:我们写在类里面他是不是会有一个隐含的this指针,那但我们想正常调用的时候是不是就成这样了:

s1(2022,6,7);
cout<<s1<<endl;//cout.operator<<(s1)

我的s1是不是就传到out的位置上了这样是不是不对阿,况且我们总不可能因为这样写成:s1<<cout是吧

想要解决这个问题很显然我们是不想要这个this指针的,那我们吧函数写在类外面不就可以了嘛至于访问它的成员变量有多种方法,这里我就选择友元函数来解决(意思是告诉我们这个类这个函数是我们的朋友,可以访问私有成员)

friend ostream& operator<<(ostream& out, const stu& d);
void operator<<(ostream& out, const stu& d)
{
    out << d._year << '-' << d._month << '-' << d._day << endl;
}

现在这个重载虽可以运行但还有一个问题:我们的返回值是不是应该重新考虑一下呢

我们平常写的时候是有可能会这样写的 cout<<d1<<d2<<d3<<endl; 所以我们输出完一个对象后还得紧接着输出下一个对象:

friend ostream& operator<<(ostream& out, const stu& d);
friend istream& operator>>(istream& in, stu& d);
ostream& operator<<(ostream& out, const stu& d)
{
    out << d._year << '-' << d._month << '-' << d._day << endl;
    return out;
}
istream& operator>>(istream& in, stu& d)
{
  in >> d._year >> d._month >> d._day;
  return in;
}

感谢大家能够看到这里,预祝大家都能收到自己心仪大厂的offer!!!

目录
相关文章
|
6天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
43 18
|
6天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
32 13
|
6天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
23 5
|
6天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
20 5
|
6天前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
23 4
|
6天前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
19 3
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
69 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
123 5
|
2月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
128 4
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
180 4