C++第八弹---类与对象(五)

简介: C++第八弹---类与对象(五)



1、运算符重载

1.1、赋值运算符重载

1. 赋值运算符重载格式

参数类型:const T&,传递引用可以提高传参效率。

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

检测是否自己给自己赋值。

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

class Date
{
public:
  Date(int year = 2022, int month = 1, int day = 1)
  {
    _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;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1(2022, 2, 2);
  Date d2 = d1;
  return 0;
}

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

 

class Date
{
public:
  Date(int year = 2022, int month = 1, int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
//private:
  int _year;
  int _month;
  int _day;
};
Date& operator=(Date& d1,const Date& d2)
{
  //成员变量必须为public访问限定符
  if (&d1 != &d2)
  {
    d1._year = d2._year;
    d1._month = d2._month;
    d1._day = d2._day;
  }
  return d1;
}
int main()
{
  Date d1(2022, 2, 2);
  Date d2 = d1;
  return 0;
}
// 编译失败:
// 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 Date
{
public:
  Date(int year, int month, int day)
  {
    _year = year;
    _month = month;
    _day = day;
  }
private:
  // 基本类型(内置类型)
  int _year = 1970;
  int _month = 1;
  int _day = 1;
  // 自定义类型
  Time _t;
};
int main()
{
  Date d1(2022,1,1);
  Date d2 =d1;
  return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实

现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
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()
  {
    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;
  s2 = s1;
  return 0;
}

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

须要实现。

1.2、前置++和后置++重载

前置++:单目操作符,将一个值+1,遵循先++再使用原则。

后置++:单目操作符,将一个值+1,遵循先使用再++原则。

前置++和后置++都是两个+操作符的组合,那怎么区分重载的是前置++还是后置++呢?

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

class Date
{
public:
  Date(int year = 2020, int month = 2, int day = 2)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  // this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
  Date& operator++()//前置++
  {
    _day += 1;
    return *this;
  }
  Date operator++(int)//后置++  加参数
  {
    //Date temp = *this;//赋值
    Date temp(*this);//拷贝  其中之一即可
    _day += 1;
    return temp;//temp是临时变量,出了函数会自动销毁,所以只能传值返回
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d(2022, 1, 1);
  Date d1;
  d1 = ++d;
  d1 = d++;
  return 0;
}

前置--后置--同前置++后置++原理,C++规定:后置--重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。uu们可以自己下去手动实现喔~。

2、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; // 日
};
int main()
{
  Date d1(2022, 1, 13);
  d1.Print();
  const Date d2(2022, 1, 13);
  d2.Print();
  return 0;
}

1、如果按照上面运行代码,d1打印的是不带const的打印函数,d2打印的是带const的打印函数。

2、如果屏蔽第一个不带const修饰的打印函数,程序依旧可以运行。因为非const类型调用const类型参数属于权限的缩小。

3、如果屏蔽第二个带有const修饰的打印函数,程序就不能正常运行。因为const类型调用非const类型参数属于权限的放大。

总结:

如果不需要改变类中成员函数或者成员变量的值,建议加上const修饰。

请思考下面的几个问题:

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

不能,此处属于权限扩大。

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

可以,此处属于权限缩小。

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

不能,此处属于权限扩大。

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

可以,此处属于权限缩小。

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

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

 

class Date
{
public:
  // 正常不写取地址重载的实质如下:
  Date* operator&()
  {
    return this;
  }
  const Date* operator&() const
  {
    return this;
  }
  // 可以手动进行修改
  //Date* operator&()
  //{
  //  return nullptr;
  //}
  //const Date* operator&() const
  //{
  //  int a = 10;
  //  return (const Date*)a;
  //}
};
int main()
{
  Date d1;
  const Date d2;
  cout << &d1 << endl;//调用取地址操作符重载
  cout << &d2 << endl;//调用const取地址操作符重载
  return 0;
}

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需

要重载,比如想让别人获取到指定的内容!

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

相关文章
|
7月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
89 0
|
3月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
169 0
|
5月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
176 12
|
6月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
128 16
|
6月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
6月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
6月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
345 6
|
6月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
7月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)