运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)

友元函数

什么是友元函数

私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。

在面向对象编程中,友元函数(friend function)是一个指定类(class)的“朋友”,该函数被允许访问该类中private、protected、public的数据成员。普通的函数并不能访问这些数据,然而宣告一个函数成为一个类的友元函数则被允许访问这些数据。

友元函数的宣告可以放在类声明的任何地方,不受访问限定关键字private、protected、public的限制。一个相似的概念是友谊类。

友谊关键字应该谨慎使用。如果一个拥有private或者protected成员的类,宣告过多的友元函数,可能会降低封装性的价值,也可能对整个设计框架产生影响。

友元函数的存在形式有?
  • 友元函数之全局函数
    现在我们有一个全局函数 distance ,通过它计算两个点之间的距离,如果直接访问肯定是不行的,编译会报错。当我们在类Point中将其声明为友元之后,就可以了。
    运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第1张图片如图:
    运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第2张图片
  • 友元函数之成员函数
    假设类A有一个成员函数,该成员函数想去访问另一个类B类中的私有成员变量。这时候则可以在第二个类B中,声明第一个类A的那个成员函数为类B的友元函数,这样第一个类A的某个成员函数就可以访问第二个类B的私有成员变量了。同样还是求取两个点之间的距离,现在我们再定义一个类 Line ,由Line 中的成员函数 distance 完成:
    运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第3张图片
  • 友元之友元类
    如上的例子,假设类 Line 中不止有一个 distance 成员函数,还有其他成员函数,它们都需要访问Point 的私有成员,如果还像上面的方式一个一个设置友元,就比较繁琐了,可以直接将 Line 类设置为 Point 的友元。

运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第4张图片

友元函数的封装性

不可否认,友元在一定程度上将类的私有成员暴露出来,破坏了信息隐藏机制,似乎是种“副作用很大的药”,但俗话说“良药苦口”,好工具总是要付出点代价的,拿把锋利的刀砍瓜切菜,总是要注意不要割到手指的。

友元的存在,使得类的接口扩展更为灵活,使用友元进行运算符重载从概念上也更容易理解一些,而且, C++ 规则已经极力地将友元的使用限制在了一定范围内,它是单向的、不具备传递性、不能被继承,所以,应尽力合理使用友元。

注意:友元的声明是不受 public/protected/private 关键字限制的。

运算符重载

运算符重载的原则

1.不能无中生有(不能定义新的运算符),也不要改变运算符的原有意义。
2.重载不能改变运算符运算对象(即操作数)的个数。
3.重载不能改变运算符的优先级与结合性。
4.重载运算符的函数不能指定默认的参数值。
5.重载运算符函数的参数应至少有一个是类对象(或类对象的引用),不能都是基本类型。

不能重载的运算符
作用域操作符:::

条件操作符:?:

点操作符:.

指向成员操作的指针操作符:->*,.*

 还有 sizeof运算符

.、.*运算符不能重载是为了保证访问成员的功能不能被改变,域运算和sizeof运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征。

函数调用运算符

运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第5张图片
使用静态变量计数
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第6张图片
使用私有成员变量

私有成员体现了封装性的特点,而静态全局变量只是起到了一个计数的作用,是共享的。除此之外,func函数只是在逻辑顺序上的执行,而fo可以无限的创建,函数对象可以体现出状态。

一般地,把这种带有状态的函数对象成为闭包—>匿名函数—>lambda表达式

下标访问运算符

运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第7张图片
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第8张图片
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第9张图片运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第10张图片在C++对下标访问运算符重载的好处
1.增加 安全性,对下标进行判断,相比C里面数组安全
2.重载【】之后,可以放在等号的左边

在什么情况下,需要加引用符号
1.防止返回值是对象的时候调用拷贝构造函数
2.允许连续赋值的时候,cout << “hello” << endl;

运算符重载—自增

1.运算符重载之普通函数形式进行重载
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第11张图片
2.运算符重载之成员函数
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第12张图片
3.运算符重载之友元函数的形式进行重载(可以直接对类的私有成员进行操作,非常方便,并且符合加法的习惯,两个操作数,没有采用隐藏this指针的方式,只有一个操作数出现,不符合加法习惯,推荐使用以友元函数的形式进行重载)
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第13张图片特别地,如果以自身状态为改变目标的重载,可以以成员函数的形式进行重载,比较方便。比如这里+=, -=, /=都可以以这种成员函数方式进行重载。
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第14张图片

特殊运算符的重载

前置和后置++的运算符重载

运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第15张图片
非常清晰,后置++首先要把表达式的值保存下来,之后对成员变量进行++,然后返回保存下来的表达式的值,注意顺序。其中后置++中参数列表中有一个int,这只表示着,后置++的一个标示,不代表传参。这是实现c++的大佬写的,大佬就是规则的制定者,记住吧少年

这里com是一个局部变量,所以要注意千万不要返回引用,这里要把引用去掉。

这里也可以看得出,前置++和后置++是有区别的,前置++的效率都说效率很高,因为前置++返回的是一个引用,是对象本身后置++返回的是一个局部对象,包含一个执行拷贝构造函数的过程,所以相比下来,效率就比前置++的 执行效率低。

看一个有意思的左值和右值
运算符重载(友元函数、运算符重载的形式、特殊运算符的重载、类型转换)_第16张图片前置++返回的是对象本身,所以可以加取地址符号,是左值。
而后置++返回的是一个局部变量,是右值。

运算符重载代码:

#include 
#include 
#include 

using std::cin;
using std::endl;
using std::cout;
using std::cerr;
using std::vector;
class String {
public:
    String()
        : _pstr(nullptr)
    {
        cout << "String()" << endl;
    }


    String(const char *pstr)
    {
        cout << "String(const char*)" << endl;
        _pstr = new char[strlen(pstr) + 1];
        strcpy(_pstr, pstr);
    }


    String(const String &pstr){
        cout << "String(const String&)" << endl;
        _pstr = new char[strlen(pstr._pstr) + 1];
        strcpy(_pstr, pstr._pstr);
    }

    ~String(){
        cout << "~String()" << endl;
        if(_pstr != nullptr){
            delete _pstr;
            _pstr = nullptr;
        }
    }
    void display() const
    {
        cout << _pstr << endl;
    }
    String &operator=(const String &);
    String &operator=(const char *);

    String &operator+=(const String &);
    String &operator+=(const char *);

    char &operator[](std::size_t index);
    const char &operator[](std::size_t index) const;

    size_t size() const;
    const char* c_str() const;

    friend bool operator==(const String &, const String &);
    friend bool operator!=(const String &, const String &);

    friend bool operator<(const String &, const String &);
    friend bool operator>(const String &, const String &);
    friend bool operator<=(const String &, const String &);
    friend bool operator>=(const String &, const String &);

    //杈撳叆杈撳嚭娴佺殑鍙互鍙傝€冩暀鏉愶紝
    friend std::ostream &operator<<(std::ostream &os, const String &s);
    friend std::istream &operator>>(std::istream &is, String &s);
private:
    char *_pstr;
};

size_t String::size() const
{
    return strlen(_pstr);
}

const char* String::c_str() const
{
    return &_pstr[0];
}

String &String::operator=(const String &str){
    cout << "String &String::operator=(const String &)" << endl;
    if(strcmp(_pstr, str.c_str())){
        _pstr = new char[strlen(str._pstr) + 1];
        strcpy(_pstr, str._pstr);
    }
    return *this;
}

String &String::operator=(const char *str){
    cout << "String &String::operator=(const char*)" << endl;
    if(_pstr){
        delete _pstr;
        _pstr = nullptr;
    }
    _pstr = new char[strlen(str) + 1];
    strcpy(_pstr, str);
    return *this;
}

String &String::operator+=(const String &str){
    cout << "String &String::operator+=(const String&)" << endl;
    size_t length1 = this->size();
    size_t length2 = str.size();
    const char *s = this->c_str();
    _pstr = new char[length1 + length2 + 1];
    strcpy(_pstr, s);
    strcat(_pstr, str._pstr);
    return *this;
}

String &String::operator+=(const char *str){
    cout << "String &String::operator+=(const char*)" << endl;
    size_t length1 = this->size();
    size_t length2 = strlen(str);
    const char *s = this->c_str();
    _pstr = new char[length1 + length2 + 1];
    strcpy(_pstr, s);
    strcat(_pstr, str);
    return *this;
}

char &String::operator[](std::size_t index){
    if(index < 0 && index > size()){
        static char p= '\0';
        cerr << "wrong index" << endl;
        return p;
    }
    else{
        return _pstr[index];
    }
}

const char &String::operator[](std::size_t index) const
{
    if(index < 0 && index > size()){
        static char p= '\0';
        cerr << "wrong index" << endl;
        return p;
    }
    else{
        return _pstr[index];
    }
}

bool operator==(const String &lhs, const String &rhs){
    if(!strcmp(lhs._pstr, rhs._pstr)){
        return true;
    }
    return false;
}
bool operator!=(const String &lhs, const String &rhs){
    if(strcmp(lhs._pstr, rhs._pstr)){
        return true;
    }
    return false;
}

bool operator<(const String &lhs, const String &rhs){
    size_t length1 = strlen(lhs._pstr);
    size_t length2 = strlen(rhs._pstr);
    size_t index = 0;
    while(length1 && length2){
        if(lhs[index] < rhs[index]){
            return true;
        }
        index++;
    }
    if(index != length2){
        return true;
    }
    return false;
}

bool operator>(const String &lhs, const String &rhs){
    size_t length1 = strlen(lhs._pstr);
    size_t length2 = strlen(rhs._pstr);
    size_t index = 0;
    while(length1 && length2){
        if(lhs[index] > rhs[index]){
            return true;
        }
        index++;
    }
    if(index != length1){
        return true;
    }
    return false;

}

bool operator<=(const String &lhs, const String &rhs){
    if(lhs == rhs || lhs < rhs){
        return true;
    }
    return false;
}

bool operator>=(const String &lhs, const String &rhs){
    if(lhs == rhs || lhs > rhs){
        return true;
    }
    return false;
}

String operator+(const String &lhs, const String &rhs){
    char *p;
    strcpy(p, lhs.c_str());
    strcat(p, rhs.c_str());
    String str = p;
    return str;
}
String operator+(const String &string, const char *str){
    char *p;
    strcpy(p, string.c_str());
    strcat(p, str);
    String s = p;
    return s;
}
String operator+(const char *str, const String &string){
    char *p;
    strcpy(p, str);
    strcat(p, string.c_str());
    String s = p;
    return s;
}

std::ostream &operator<<(std::ostream &os, const String &s){
    os << s._pstr;
    return os;
}
std::istream &operator>>(std::istream &is, String &s){
    /* is >> s._pstr; */
    /* if(!is){ */
    /*     s = "oops"; */
    /* } */
    /* return is; */
    if(s._pstr){
        delete [] s._pstr;
        s._pstr = nullptr;
    }
    vector<char> buffer;
    char ch;
    while((ch = is.get()) != '\n'){
        buffer.push_back(ch);
    }
    s._pstr = new char[buffer.size() + 1]();
    strncpy(s._pstr, &buffer[0], buffer.size());
}

int main()
{

    String str1("hello world");
    cout << "str1 = ";
    str1.display();
    cout << endl;

    String str2(str1);
    cout << "str2 = ";
    str2.display();
    cout << endl;

    String str3("nihao");
    cout << "str3---before change = ";
    str3.display();
    str3 = str2;
    cout << "str3---after change = ";
    str3.display();
    cout << endl;

    char *s = (char *)"sayounala";
    String str4 = s;
    cout << "str4 = ";
    str4.display();
    cout << endl;

    String str5("baby");
    cout << "str5 = ";
    str5.display();
    str5 += str4;
    cout << "str5 = ";
    str5.display();
    cout << endl;


    String str6("baobao");
    cout << "str6 = ";
    str6.display();
    str6 += s;
    cout << "str6 = ";
    str6.display();
    cout << endl;

    String a = "123";
    String b = "123";
    String c = "1234";
    String d = "234";

    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;

    String e;
    cout << "Plz input a number:";
    cout << endl;

    cin >> e;
    
    cout << "e = " << e << endl;

    cout << "(a == b) = " << (a == b) << endl;
    cout << "(a == c) = " << (a == c) << endl;
    cout << "(a != c) = " << (a != c) << endl;
    cout << "(a > c) = " << (a > c) << endl;
    cout << "(a < c) = " << (a < c) << endl;
    cout << "(a < d) = " << (a < d) << endl;
    cout << "(a > d) = " << (a > d) << endl;


    return 0;
}


你可能感兴趣的:(C++)