day04(C++)运算符重载

目录

运算符重载

1. 友元 friend

1.1 概念(掌握)

1.2 友元函数(掌握)

1.3 友元类(熟悉)

1.4 友元成员函数(熟悉)

2. 运算符重载(重点)

2.1 概念 (掌握)

2.2 友元函数运算符重载(重点)

2.3 成员函数运算符重载(重点)

2.4 赋值运算符(重点)与类型转换运算符重载(熟悉)

2.5 注意事项(掌握)

3. string 字符串类(熟悉)


运算符重载

                --------主要介绍函数重载的一种特殊形式——运算符重载,可以让程序拥有更好的灵活性

1. 友元 friend

1.1 概念(掌握)

定义:

类实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,仅能通过类的成员函数才能读写。如果数据成员定义为公共的,则又破坏了封装性。但是某些情况下,需要频繁读写类的成员,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

友元是一种定义在类外部的普通函数,但他需要在类体内进行说明,为了和该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是他能够访问类中的所有成员

作用:

在于提高程序的运行效率,但是,他破坏了类的封装性和隐藏性,使得非成员函数能够访问类的私有成员。导致程序维护性变差,因此使用友元要慎用

友元较为实际的应用是在运算符重载,这种应用可以提高软件系统的灵活性。

分类:

  • 友元函数
  • 友元类
  • 友元成员函数

1.2 友元函数(掌握)

友元函数是一种“声明”在类内,实际在类外的普通函数。

#include <iostream>

using namespace std;

class Girl
{
private:
    int age;

public:
    Girl(int age):age(age){}

    int get_age() const
    {
        cout << &age << endl;
        return 18;
    }

    // 1. "声明"友元函数
    friend void access_true_age(Girl&);
};

// 2. 定义友元函数
void access_true_age(Girl& g)
{
    // 突破权限
    cout << &g.age << endl;
    cout << "真实年龄:" << g.age << endl;
    // 修改
    g.age = 18;
    cout << "修改后年龄:" << g.age << endl;
}

int main()
{
    Girl g(45);
    cout << g.get_age() << endl;
    // 通过友元函数访问Girl的年龄
    access_true_age(g);

    return 0;
}

需要注意的是:

  • 由于不属于类的成员函数,因此友元函数没有this指针,访问类的成员只能通过对象。
  • 友元函数在类中的“声明”可以写在类的任何部分,不受权限修饰符的影响。
  • 理论上一个友元函数可以是多个类的友元函数,只需要在各个类中分别“声明”。

1.3 友元类(熟悉)

当一个类B成为了另一个类A的友元类时,类B可以访问类A的所有成员。

需要注意的是:

  • 友元关系是单向的,不具有交换性

        如果类B是类A的友元类,类A不一定是类B的友元类。

  • 友元关系不具有传递性

        如果类C是类B的友元类,类B是类A的友元类,类C不一定是类A的友元类。

  • 友元关系不能被继承
#include <iostream>

using namespace std;

class A
{
private:
    string str = "A私有";

    // “声明”友元类
    friend class B;
};

class B
{
public:
    void func(A& a)
    {
//        cout << this->str << endl; 错误:this是B对象不是A对象
        cout << a.str << endl;
        a.str =  "我改了";
        cout << a.str << endl;
    }
};

int main()
{
    A a;
//    cout << a.str << endl; 错误
    B b;
    b.func(a);

    return 0;
}

1.4 友元成员函数(熟悉)

在友元类的任何成员函数中都可以访问其他类的成员,但是友元成员函数把友元范围限制在一个成员函数中。

例如,类B的某个成员函数称为了类A的友元成员函数,这样类B的该成员函数就可以访问类A的所有成员了。

#include <iostream>

using namespace std;

// 3. 因为第二步中用到了类A,提前声明类A
class A;

// 2. 编写类B,并真正声明友元成员函数
class B
{
public:
    void func(A&);
};

class A
{
private:
    string str = "A私有";

    // 1. 确定友元的函数格式并“声明”
    friend void B::func(A&);
};

// 4. 类外定义友元成员函数
void B::func(A & a)
{
    //  cout << this->str << endl; 错误:this是B对象不是A对象
    cout << a.str << endl;
    a.str =  "我改了";
    cout << a.str << endl;
}


int main()
{
    A a;
    //    cout << a.str << endl; 错误
    B b;
    b.func(a);

    return 0;
}

2. 运算符重载(重点)

2.1 概念 (掌握)

如果把运算符看做是一个函数,则运算符也可以像函数一样重载。

C++中预定义的运算符的操作对象只能是基本数据类型。但实际上对于很多用户的自定义类型,也需要类似的运算操作、这时可以在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型,执行特定的操作。

可以被重载的运算符:

算术运算符:+、-、*、/、%、++、--

位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)

逻辑运算符:!、&&、||

比较运算符:<、>、>=、<=、==、!=

赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=

其他运算符:[]、()、->、,、new、delete、new[]、delete[]

不被重载的运算符:

成员运算符“.”、指针运算符“*”、三目运算符“? :”、sizeof、作用域“::”

2.2 友元函数运算符重载(重点)

#include <iostream>

using namespace std;

/**
 * @brief The Integer class 整数类
 */
class Integer
{
private:
    int value;

public:
    Integer(int value):value(value){}

    int get_value() const
    {
        return value;
    }

    // 友元函数“声明”
    friend Integer operator +(const Integer&,const Integer&);
    friend Integer operator ++(Integer&); // 前置
    friend Integer operator ++(Integer&,int); // 后置
};

Integer operator +(const Integer& i1,const Integer& i2)
{
    return i1.value + i2.value; // 隐式调用构造函数
}

Integer operator ++(Integer& i)
{
    return ++i.value; // 隐式调用构造函数
}

Integer operator ++(Integer& i,int)
{
    return i.value++; // 隐式调用构造函数
}

int main()
{
    Integer i1(1);
    Integer i2(2);
    Integer i3 = i1 + i2;
    cout << i3.get_value() << endl; // 3
    cout << (i1++).get_value() << endl; // 1
    cout << (++i1).get_value() << endl; // 3

    return 0;
}

2.3 成员函数运算符重载(重点)

成员函数运算符重载与友元函数运算符重载的最大区别是:友元函数运算符重载的第一个参数,在成员函数中使用this指针代替,即使用成员函数重载的运算符相比友元函数的参数少一个。

#include <iostream>

using namespace std;

/**
 * @brief The Integer class 整数类
 */
class Integer
{
private:
    int value;

public:
    Integer(int value):value(value){}

    int get_value() const
    {
        return value;
    }

    // 声明成员函数
    Integer operator +(const Integer&); // 双目
    Integer operator ++(); // 前置

    Integer operator ++(int) // 后置
    {
        return this->value++;
    }
};

Integer Integer::operator +(const Integer& i)
{
    return this->value + i.value;
}

Integer Integer::operator ++()
{
    return ++this->value;
}


int main()
{
    Integer i1(1);
    Integer i2(2);
    Integer i3 = i1 + i2;
    cout << i3.get_value() << endl; // 3
    cout << (i1++).get_value() << endl; // 1
    cout << (++i1).get_value() << endl; // 3

    return 0;
}

2.4 赋值运算符(重点)与类型转换运算符重载(熟悉)

如果程序员不写赋值运算符重载函数,编译器会为这个类自动添加一个赋值运算符重载函数,且此函数支持链式调用,因此只能使用成员函数运算符重载(不使用友元)。

结合之前内容,处理深浅拷贝时应该同时处理赋值运算符。

#include <iostream>

using namespace std;


class Value
{
public:
    int value = 0;
    // 1. 构造函数
    // 2. 拷贝构造
    // 3. 析构函数
    // 4. 赋值运算符重载
    // 编译器自动添加:
    Value& operator =(const Value& v)
    {
        value = v.value;
        return *this;
    }
};


int main()
{
    Value v; // 构造函数
    Value v2(v); // 拷贝构造
    Value v3 = v2; // 拷贝构造
    v.value = 1;
    v3 = v; // 赋值运算符
    cout << v3.value << endl; // 1

    return 0;
}

类型转换运算符与赋值运算符的符号都是=,因此类型转换运算符重载函数的格式比较特殊,以便于与赋值运算符重载进行区分,同样类型转换运算符重载函数也只支持成员函数运算符重载。

#include <iostream>

using namespace std;


class Value
{
private:
    int value;

public:
    Value(int value):value(value){}

    int get_value() const
    {
        return value;
    }

    // 类型转换运算符重载函数
    operator int()
    {
        return value;
    }
};


int main()
{
    // int → Value
    Value v = 1; // 隐式构造

    // Value → int
    int i = v; // 类型转换运算符重载函数
    cout << i << endl; // 1

    return 0;
}

2.5 注意事项(掌握)

  • 运算符重载限制在C++已有的运算符范围内,不能创建新的运算符。
  • 运算符重载不能改变运算符的优先级、结合性、操作数和语法结构。
  • 运算符重载不能改变基本类型的计算规则,只能更改包含自定义类型的计算规则。
  • 运算符重载实现的功能应该与原运算符相近。
  • 运算符重载函数不支持参数默认值。
  • 通常单目运算符使用成员函数重载,双目运算符使用友元函数重载。

3. string 字符串类(熟悉)

#include <iostream>
#include <string.h>

using namespace std;

int main()
{
    string s; // 生成一个空字符串
    cout << "判断字符串是否为空:" <<  s.empty() << endl; // 1
    string s1 = "Thursday"; // 隐式调用构造函数,参数const char*
    string s2("Thursday"); // 显式调用上面的构造函数
    cout << (s1 == s2) << endl; // 1
    string s3 = s1; // 隐式调用拷贝构造
    string s4(s2); // 显式调用拷贝构造
    cout << (s3 == s4) << endl; // 1
    s = s4; // 赋值运算符
    cout << s << endl; // "Thursday"

    // 参数1:const char* 原字符串
    // 参数2:保留几个字符
    string s5("ABCDEFG",2);
    cout << s5 << endl; // AB

    s = "ABCDEFG";
    // 参数1:string 原字符串
    // 参数2:不保留前几个字符
    string s6(s,2);
    cout << s6 << endl; // CDEFG

    // 参数1:字符串长度
    // 参数2:字符内容
    string s7(6,'A');
    cout << s7 << endl; // AAAAAA
    // 交换
    swap(s6,s7);
    cout << s6 << " " << s7 << endl; // AAAAAA CDEFG

    s = s6+s7; // 拼接
    cout << s << endl; // AAAAAACDEFG

    // 向后追加
    s.append("123");
    cout << s << endl; // AAAAAACDEFG123

    // 向后追加单字符
    s.push_back('%'); // AAAAAACDEFG123%

    // 插入
    // 参数1:插入的位置
    // 参数2:插入的内容
    s.insert(1,"222"); // A222AAAAACDEFG123%

    // 参数1:删除的起始位置
    // 参数2:删除的字符数
    s.erase(4,10);
    cout << s << endl; // A222123%

    // 参数1:替换的起始位置
    // 参数2:替换的字符数
    // 参数3:替换的内容
    s.replace(0,3,"******");
    cout << s << endl; // ******2123%

    s.clear(); // 清空
    cout << s.length() << endl; // 0

    char c[20];
    s = "1234567890";
    // 参数1:拷贝的目标
    // 参数2:拷贝的字符数
    // 参数3:拷贝的起始位置
    s.copy(c,3,1);
    cout << c << endl; // 234

    // C → C++ 直接赋值即可
    // char* → string
    char* c1 = "Tom";
    char c2[] = "Jerry";
    string sc1 = c1;
    string sc2 = c2;
    cout << sc1 << "&" << sc2 << endl; // Tom&Jerry

    // C++ → C
    // string → char[]
    s = "abcd";
    char ch[10];
    strcpy(ch,s.c_str()); // c_str()返回值的const char*不稳定
    cout << ch << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值