C++从入门到精通——类的6个默认成员函数之赋值运算符重载

这篇具有很好参考价值的文章主要介绍了C++从入门到精通——类的6个默认成员函数之赋值运算符重载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

类的6个默认成员函数:如果一个类中什么成员都没有,简称为空类。

空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

class Date {};

C++从入门到精通——类的6个默认成员函数之赋值运算符重载,C++从入门到精通,c++,java,开发语言,考研,蓝桥杯,学习方法,程序人生


一、运算符重载

定义

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

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

函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@

  • 重载操作符必须有一个类类型参数 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

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

  • .* :: sizeof ?: . 注意以上5个运算符不能重载。

实例

// 全局的operator==
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 operator==(const Date& d1, const Date& d2)
{
    return d1._year == d2._year
        && d1._month == d2._month
        && d1._day == d2._day;
}
void Test()
{
    Date d1(2018, 9, 26);
    Date d2(2018, 9, 27);
    cout << (d1 == d2) << endl;
}
class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    // bool operator==(Date* this, const Date& d2)
    // 这里需要注意的是,左操作数是this,指向调用函数的对象
    bool operator==(const Date& d2)
    {
        return _year == d2._year;
        && _month == d2._month
            && _day == d2._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

注意要点

同时存在全局运算符重载和类里的运算符重载,编译器会优先调用类里的运算符重载

bool operator==(const Date& d1, const Date& d2)
{
    return d1._year == d2._year
        && d1._month == d2._month
        && d1._day == d2._day;
}
class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    // bool operator==(Date* this, const Date& d2)
    // 这里需要注意的是,左操作数是this,指向调用函数的对象
    bool operator==(const Date& d2)
    {
        return _year == d2._year;
        && _month == d2._month
            && _day == d2._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

函数重载与运算符重载的区别

不同点

函数重载和运算符重载是C++中两个相关但不同的概念。

函数重载是指在同一个作用域中定义多个具有相同名称但参数列表不同的函数。这样做的目的是为了提供更灵活的函数调用方式,使得同一个函数名可以根据不同的参数类型或参数个数执行不同的操作。

运算符重载是指在C++中允许自定义类的成员函数或非成员函数来重新定义运算符的行为。通过运算符重载,可以为自定义的类创建与内置类型相似的运算符行为,使得自定义类的对象可以像内置类型一样进行运算。

总结:函数重载是针对函数进行的,通过改变参数列表来定义多个同名函数;而运算符重载是针对运算符进行的,通过重新定义运算符的行为来实现与内置类型相似的运算。

相似点

函数重载和运算符重载在某些方面是相似的,它们都是通过改变函数或运算符的行为来提供更灵活的功能。

  1. 名称相同:函数重载和运算符重载都是使用相同的名称来定义多个不同的行为。

  2. 参数列表变化:函数重载通过改变参数列表来定义多个同名函数,而运算符重载通过改变函数参数或者在类中定义成员函数重载运算符。

  3. 增加灵活性:无论是函数重载还是运算符重载,都可以根据需要定义不同的行为,使得代码更加灵活易用。

  4. 增加可读性:函数重载和运算符重载可以使代码更具可读性,因为可以根据函数名或运算符符号来推测其功能。

尽管函数重载和运算符重载在某些方面相似,但它们的目的和应用场景有所不同。函数重载用于定义同一功能的不同实现,而运算符重载用于为自定义类创建与内置类型相似的运算符行为。

总结

  • 函数重载:可以让函数名相同,参数不同的函数同时存在

  • 运算符重载:让自定义类型可以使用运算符,并且控制运算符的行为,增强可读性

  • 他们之间各论各的,没有关系

  • 多个同一运算符重载可以构成函数重载

二、赋值运算符重载

赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
class Date
{
public:
    Date(int year = 1900, 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;
    }

    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;
};

赋值运算符重载要点

重载要点

  1. 赋值运算符只能重载成类的成员函数不能重载成全局函数
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 =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
C++从入门到精通——类的6个默认成员函数之赋值运算符重载,C++从入门到精通,c++,java,开发语言,考研,蓝桥杯,学习方法,程序人生

  1. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
    注意:
    • 内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
    • 跟拷贝构造类似。
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
{
private:
    // 基本类型(内置类型)
    int _year = 1970;
    int _month = 1;
    int _day = 1;
    // 自定义类型
    Time _t;
};
int main()
{
    Date d1;
    Date d2;
    d1 = d2;
    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;
}

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

C++从入门到精通——类的6个默认成员函数之赋值运算符重载,C++从入门到精通,c++,java,开发语言,考研,蓝桥杯,学习方法,程序人生

传值返回和传址返回要点

可以看到传值和传址在遇到不同问题时有不同的表现,如下,在运算符重载的问题下,传址调用比传值调用的效率更高,关于为什么要返回*this,见下面
C++从入门到精通——类的6个默认成员函数之赋值运算符重载,C++从入门到精通,c++,java,开发语言,考研,蓝桥杯,学习方法,程序人生
正常的赋值表达式都是支持连续赋值的,如果我们使用Date作为返回值,可以见到效率太低了,相比指向返回Date的引用效率更高

d1 = d2 = d3
 Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }

        return *this;
    }

根据上式,我们可以见到,我们是把d3的值赋给d2,而d2的地址存放在this指针里,我们需要对this指针进行解引用才能得到d2的地址,得到d2的地址后,我们便可以进行下一步的赋值

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

示例

class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    // 前置++:返回+1之后的结果
    // 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
    Date& operator++()
    {
        _day += 1;
        return *this;
    }
    // 后置++:
    // 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
    // C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
        // 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1
        //而temp是临时对象,因此只能以值的方式返回,不能返回引用
        Date operator++(int)
    {
        Date temp(*this);
        _day += 1;
        return temp;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d;
    Date d1(2022, 1, 13);
    d = d1++;    // d: 2022,1,13   d1:2022,1,14
    d = ++d1;    // d: 2022,1,15   d1:2022,1,15
    return 0;
}

概念

在C++中,++操作符可以被重载为前置++和后置++两种形式。

前置++表示在操作数之前增加1,并返回增加后的值。

后置++表示在操作数之后增加1,并返回增加前的值。

下面是前置++和后置++的重载函数:

class MyClass {
public:
    MyClass& operator++() {          // 前置++
        // 在此处增加1的逻辑
        return *this;
    }
    
    MyClass operator++(int) {       // 后置++
        MyClass temp(*this);        // 复制当前对象
        // 在此处增加1的逻辑
        return temp;                // 返回增加前的对象
    }
};

四、深挖operator

在C++中,输出流操作符 << 可以被重载用于自定义类型的对象,以便在流中输出该对象的内容。

友元函数

具体请看这篇文章,C++从入门到精通——友元
下面是重载流输出操作符的示例:

#include <iostream>

class MyClass {
public:
    int value;
    
    MyClass(int val) : value(val) {}
    
    friend std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
        os << obj.value;
        return os;
    }
};

int main() {
    MyClass obj(42);
    
    std::cout << obj << std::endl;  // 输出对象的内容
    
    return 0;
}

在上述示例中,我们定义了一个名为MyClass的类,该类具有一个整型成员变量value。我们将流输出操作符 << 声明为友元函数,并在函数中实现输出对象的内容。在主函数中,我们创建了一个名为objMyClass对象,并使用流输出操作符将其内容输出到标准输出流中。

输出结果将是 “42”。

注意,我们可以通过重载流输出操作符来控制输出对象的格式和内容。

模拟实现

class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
   	void operator<<(ostream& out)
   	{
		out<<_year<<"年"<<_month<<"月"<<_day<<"日"<<endl;
	}
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1;
    d1.operator<<(cout);
    d1 << cout;
    return 0;
}

注意以下情况,因为在类里定义的函数,第一个对象永远是this指针,写成cout<<d1是错误的写法,即函数重载中,参数顺序和操作数顺序是一致的。

d1 << cout;

我们可以通过使用在类外面定义一个新函数,或者在类里使用上面的示例。下面定义的函数直接运行的话是会出错的,因为_year,_month,_day是私有的。

void operator<<(ostream& out,const Date& d)
{
	out<<d._year<<"年"<<d._month<<"月"<<d._day<<"日"<<endl;
}

以此类推,流输入也是同理

友元函数

友元函数是指在类的定义中,声明为友元的函数可以直接访问该类的私有成员和保护成员。友元函数可以是全局函数,也可以是其他类的成员函数。在C++中,使用关键字friend来声明友元函数。友元函数的定义通常在类的外部。

友元函数的特点是可以绕过类的访问权限,直接访问类的私有成员和保护成员。这在一些特殊情况下很有用,例如需要在其他类中对该类的私有成员进行操作或者需要在全局函数中操作该类的私有成员。

需要注意的是,友元函数并不是类的成员函数,因此在调用时不需要通过对象来访问,可以直接使用函数名进行调用。另外,由于友元函数不属于类的成员函数,因此不能使用this指针。

友元函数的具体用法可以分为两种情况:

  1. 全局函数作为友元函数:全局函数可以在类的外部定义,并通过friend关键字声明为友元函数。在全局函数的定义中,可以直接访问该类的私有成员和保护成员。
  2. 对象的成员函数作为友元函数:在另一个类的成员函数中通过友元关键字将该类的成员函数声明为友元函数。在友元函数的定义中,可以直接访问该类的私有成员和保护成员。

需要注意的是,友元函数并不是类的成员函数,因此不能直接访问类的成员变量和成员函数。如果需要访问类的成员变量和成员函数,可以通过对象来访问。

友元函数的使用应该谨慎,因为它破坏了封装性原则,导致代码可读性和可维护性降低。在设计类的时候,应该尽量避免使用友元函数,而是通过成员函数来操作类的私有成员和保护成员。只有在确实有必要的情况下,才考虑使用友元函数。文章来源地址https://www.toymoban.com/news/detail-858039.html


到了这里,关于C++从入门到精通——类的6个默认成员函数之赋值运算符重载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解

    🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。 🎯每日格言:每日努力一点点,技术变化看得见。 我们生活中也有继承的例子,例如:小明继承了孙老师傅做拉面的手艺。继承就是一种延续、复用的方式。C++为了提高代码的可复用性,引入了继承机制,

    2024年04月10日
    浏览(35)
  • C++类的默认成员函数

    什么是默认函数? 默认函数就是当你使用这个类对象时,这个类会自动调用的函数C++中有六个默认成员函数,并且作用各不相同,下面我们来一一进行介绍 什么是构造函数?构造函数是干什么的? 什么是析构函数?析构函数是干什么的? 我们以栈为例,每一次我们在使用栈的时

    2024年02月02日
    浏览(28)
  • 【C++】类的默认成员函数(下)

    🔥 博客主页 : 小羊失眠啦. 🎥 系列专栏 : 《C语言》 《数据结构》 《C++》 《Linux》 《Cpolar》 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 本章主要内容为认识与学习C++非常重要的概念—— 运算符重载 。通过 日期类 的实现,逐步学习各个运算符重载的实现方法即含义。6个默

    2024年03月18日
    浏览(34)
  • 【C++】类的默认成员函数----const成员函数(超详细解析)

    目录 一、前言 二、const成员函数  🍎const修饰类的成员函数  💦问题1  💦问题2 💦针对const成员函数的常考面试题(重点!!) 🍐取地址及const取地址操作符重载 三、共勉    在我们前面学习的 类 中,我们会定义 成员变量 和 成员函数 ,这些我们自己定义的函数都是普

    2024年04月14日
    浏览(28)
  • C++:类的六个默认成员函数

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C++》 本篇博客作为C++知识总结,我们来认识类的六个默认成员函数。 下面我主要以日期类作为示例显示。 构造函数 是一个特殊的成员函数,名字与类名相同,创建类类型对象时(实例化类)由编译器自动调用,以保

    2024年02月08日
    浏览(37)
  • C++中类的6个默认成员函数 【拷贝构造函数】

    在前几章学习对象的时候,我们有的时候需要一个与已存在对象一某一样的新对象 那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢? 拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时

    2024年02月20日
    浏览(38)
  • 【C++】:拷贝构造函数与赋值运算符重载的实例应用之日期类的实现

    🔑日期类的实现,将按以下声明依次进行,其中因为Print函数比较短,直接放到类里面让其变成内联函数 🔑而我们实现日期类经常要用到某月有多少天,在这里先把获得某月有多少天的函数实现出来。实现时先检查传参有没有问题,在注意把数组设置成静态的,出了作用域

    2024年02月08日
    浏览(49)
  • 【C++】继承的基本特性(定义,赋值转换,友元,静态成员,虚拟继承,默认成员函数,作用域)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。 Person是父类,也称作基类。Student是子类,也称作派生类 总结: 基类private成员

    2024年02月14日
    浏览(32)
  • 【C++技能树】令常规运算符用在类上 --类的六个成员函数II

    Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我吧!你定不会失望。 C++中为了增强代码的可读性,加入了运算符的重载,与其他函数重载一样。其命名格式如下: 返回值类型 operator操作符(参数列表) 但并不是所有运算符都可以重载的: 不可以自定

    2024年02月05日
    浏览(31)
  • 【C++】类和对象②(类的默认成员函数:构造函数 | 析构函数)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 类的6个默认成员函数 构造函数 概念 构造函数的特性及用法 析构函数 概念 析构函数的特性及用法 结语 本篇主要内容:类的6个默认成员函数中的 构造函数 和 析构函数 进入到类和对象内容的第二节,上篇博客中介绍了

    2024年04月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包