C/C++知识点之C++语言学习(八)——操作符重载
小标 2019-04-22 来源 : 阅读 1062 评论 0

摘要:本文主要向大家介绍了C/C++知识点之C++语言学习(八)——操作符重载,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点之C++语言学习(八)——操作符重载,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。


一、操作符重载基础


1、操作符重载的语法


通过operator关键字可以定义特殊的函数,operator本质是通过函数重载操作符。


Type operator operatorname(const Type p1, const Type p2)
{
    Type ret;

    return ret;
}


2、友元函数重载操作符


可以将操作符重载函数声明为友元函数。


#include

using namespace std;

class Complex
{
public:
    Complex(float x=0, float y=0)
    {
        this->x = x;
        this->y = y;
    }
    void print()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
    friend const Complex operator+(const Complex &c1,const Complex &c2);
private:
    float x;
    float y;
};
const Complex operator+(const Complex &c1,const Complex &c2)
{
    return Complex(c1.x + c2.x,c1.y + c2.y);
}
int main(int argc, char *argv[])
{
    Complex c1(2,3);
    Complex c2(3,4);
    c1.print();
    c2.print();
    Complex c3 = c1 + c2;
    c3.print();
    Complex c4 = operator+(c1,c2);
    c4.print();

    return 0;
}


上述代码中,编译器会检查是否有可用的操作符重载函数,因此Complex c3 = c1 + c2;代码也是合法的。


3、成员函数重载操作符


将操作符重载函数定义为类成员函数。


#include

using namespace std;

class Complex
{
public:
    Complex(float x=0, float y=0)
    {
        this->x = x;
        this->y = y;
    }
    void print()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
    const Complex operator+(const Complex &another)
    {
        cout << "member function." << endl;
        return Complex(this->x + another.x, this->y + another.y);
    }
    friend const Complex operator+(const Complex &c1,const Complex &c2);
private:
    float x;
    float y;
};
const Complex operator+(const Complex &c1,const Complex &c2)
{
    cout << "friend global function." << endl;
    return Complex(c1.x + c2.x,c1.y + c2.y);
}
int main(int argc, char *argv[])
{
    Complex c1(2,3);
    Complex c2(3,4);
    c1.print();
    c2.print();
    //成员函数
    Complex c3 = c1 + c2;
    c3.print();
    //成员函数
    Complex c4 = c1.operator +(c2);
    c4.print();
    //全局函数
    Complex c5 = operator+(c1,c2);
    c4.print();

    return 0;
}


操作符重载函数作为类的成员函数时,比全局操作符重载函数少一个参数,不需要依赖友元就可以完成操作符重载,编译器会优先在类的成员函数中查找操作符重载函数。因此Complex c3 = c1 + c2;代码会优先调用类的操作符重载成员函数。


4、操作符重载的规则


操作符重载的规则:
A、C++不允许用户自己定义新的运算符,只能对已有的 C++运算符进行重载。
B、C++语言中大部分运算符都可以重载,成员选择符(.)、成员对象选择符(.*)、域解析操作符(::)、条件操作符(?:)、sizeof不可以重载。除了赋值操作符(=)外,基类中重载的操作符都将被派生类继承。
C、重载不能改变运算符运算对象(即操作数)的个数。
D、重载不能改变运算符的优先级别。
E、重载不能改变运算符的结合性。
F、重载运算符的函数不能有默认的参数
G、重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。参数不能全部是 C++的标准类型,以防止用户修改用于标准类型数据成员的运算符的性质。
H、用于类对象的运算符一般必须重载,但有两个例外,运算符”=“和运算符”&“不必用户重载。
I、应当使重载运算符的功能类似于该运算符作用于标准类型数据时候时所实现的功能。
J、运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数。
K、赋值操作符只能重载为成员函数


二、操作符重载实例


1、双目运算符重载


形式:L#R
全局函数:operator#(L,R);
成员函数:L.operator#(R)
operator+=实例:


#include
using namespace std;
class Complex
{
public:
    Complex(double x = 0, double y = 0)
    {
        this->x = x;
        this->y = y;
    }
    void print()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
    Complex& operator +=(const Complex &c)
    {
        this->x += c.x;
        this->y += c.y;
        return * this;
    }
private:
    double x;
    double y;
};


2、单目运算符重载


形式:#M 或 M#
全局函数:operator#(M)
成员函数:M.operator#()
operator-实例:


#include
using namespace std;
class Complex
{
public:
    Complex(double x = 0, double y = 0)
    {
        this->x = x;
        this->y = y;
    }
    void print()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
    const Complex operator-(void) const
    {
        return Complex(-x,-y);
    }
private:
    double x;
    double y;
};


3、流输入输出运算符重载


函数形式


    istream & operator>>(istream &,自定义类&);
    ostream & operator<<(ostream &,自定义类&);


流输入输出运算符重载通过友元来实现,避免修改C++的标准库。
operator<< 和operator>>实例:


class Complex
{
public:
    Complex(double x = 0, double y = 0)
    {
        this->x = x;
        this->y = y;
    }
    void print()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
    friend ostream & operator<<(ostream &os, const Complex & c);
    friend istream & operator>>(istream &is, Complex &c);
private:
    double x;
    double y;
};
ostream & operator<<(ostream &os, const Complex & c)
{
    os<<"("<<c.x<<","<<c.y<<")";
    return os;
}
istream & operator>>(istream &is, Complex &c)
{
    is>>c.x>>c.y;
    return is;
}


三、操作符重载总结


1、不可以重载的操作符


. (成员访问运算符)
.* (成员指针访问运算符)
:: (域运算符)
sizeof (长度运算符)
?: (条件运算符)


2、只能重载为成员函数的运算符


= 赋值运算符
[] 下标运算符
() 函数运算符
-> 间接成员访问
编译器默认重载了赋值运算符,但编译器默认重载的赋值操作符仅完成浅拷贝,需要深拷贝操作必须自定义重载赋值操作符。


3、运算符重载与友元


A、一个操作符的左右操作数不一定是相同类型的对象,这就涉及到将该操作符函数定义为谁的友元,谁的成员问题。
B、一个操作符函数,被声明为哪个类的成员,取决于该函数的调用对象(通常是左操作数)。
C、一个操作符函数,被声明为哪个类的友员,取决于该函数的参数对象(通常是右操作数)。


#include
using namespace std;
class Mail;
class Sender
{
public:
    Sender(string s):_addr(s){}
    Sender& operator<<(const Mail & mail); //成员
private:
    string _addr;
};
class Mail
{
public:
    Mail(string _t,string _c ):_title(_t),_content(_c){}
    friend Sender& Sender::operator<<(const Mail & mail);
    //友元
private:
    string _title;
    string _content;
};
Sender& Sender::operator<<(const Mail & mail)
{
    cout<<"Address:"<<mail._addr<<endl;
    cout<<"Title :"<<mail._title<<endl;
    cout<<"Content:"<<mail._content<<endl;
    return *this;
}
int main()
{
    Sender sender("guilin_wang@163.com");
    Mail mail("note","meeting at 3:00 pm");
    Mail mail2("tour","One night in beijing");
    sender<<mail<<mail2;
    return 0;
}


5、操作符重载的陷阱


A、逻辑运算操作符:
运算符重载本质是函数重载,C++对逻辑操作符重载时将逻辑操作符定义为函数,但是由于函数参数的计算次序是不确定的,导致逻辑操作符原生的短路法则将失效,因此不推荐对逻辑操作符进行重载。工程实践中需要使用重载比较操作符等方法避免重载逻辑操作符的陷阱,直接使用成员函数代替逻辑操作符重载,使用全局函数对逻辑操作符进行重载。


#include

using namespace std;

class Test
{
public:
    Test(int ok = true)
    {
        this->ok = ok;
    }
    int value()const
    {
        cout << "call value(),ok = " << ok << endl;
        return ok;
    }
    Test operator +(const Test& another)
    {
        this->ok += another.ok;
        return *this;
    }
private:
    int ok;
};
//&&操作符重载函数
bool operator &&(const Test& left, const Test& right)
{
    return left.value() && right.value();
}
//||操作符重载函数
bool operator ||(const Test& left, const Test& right)
{
    return left.value() || right.value();
}

Test func(Test test)
{
    cout << "Test func(Test test): i = "<< test.value() <<endl;
    return test;
}

int main(int argc, char *argv[])
{
    Test test0(0);
    Test test1(1);
    Test test2(2);
    Test test3(3);
    if(test0 && test1)
    {
        cout << "result is true" << endl;
    }
    else
    {
        cout << "result is false" << endl;
    }
    /*****************************
     *call value(),ok = 0
     *result is false
     * 上述测试代码:短路法则正常
     * **************************/
    cout << endl;
    if(operator &&(func(test0), func(test1)))
    {
        cout << "result is true" << endl;
    }
    else
    {
        cout << "result is false" << endl;
    }
    cout << endl;
    /*****************************
     *call value(),ok = 1
     *Test func(Test test): i = 1
     *call value(),ok = 0
     *Test func(Test test): i = 0
     *call value(),ok = 0
     *result is false
     *上述测试代码:短路法则失效
     * **************************/
    if((test2 + test3) && test0)
    {
        cout << "result is true" << endl;
    }
    else
    {
        cout << "result is false" << endl;
    }
    cout << endl;
    /*****************************
     *call value(),ok = 5
     *call value(),ok = 0
     *result is false
     * **************************/
    if(test0 || test1)
    {
        cout << "result is true" << endl;
    }
    else
    {
        cout << "result is false" << endl;
    }
    cout << endl;
    /*****************************
     *call value(),ok = 0
     *call value(),ok = 1
     *result is true
     * **************************/
    if(operator &&(test0, test1))
    {
        cout << "result is true" << endl;
    }
    else
    {
        cout << "result is false" << endl;
    }
    cout << endl;
    /*****************************
     *call value(),ok = 0
     *result is false
     *上述测试代码:短路法则正常
     * **************************/
    if(operator ||(test0 + test1, test3))
    {
        cout << "result is true" << endl;
    }
    else
    {
        cout << "result is false" << endl;
    }
    /*****************************
     *call value(),ok = 1
     *result is true
     *上述测试代码:短路法则正常
     * **************************/

    return 0;
}


上述测试代码中,如果逻辑操作符的操作数中是需要计算的函数调用,短路法则可能会失效。
B、逗号操作符:
可以使用全局函数对逗号操作符进行重载,重载函数的参数必须有一个是类类型,返回值类型必须是引用。


class& operator,(const class& a, const class& b)
{
    return const_cast(b);
}


重载逗号操作符后,逗号表达式无法严格从左向右计算表达式,不能重载逗号操作符,重载后的逗号操作符没有了原生的语义。
原因:操作符的重载本质是函数调用,函数调用在进入函数体前需要完成所有参数的计算,参数的计算次序是不确定的,因此重载逗号操作符后无法保证逗号操作符的原生语义。
逗号操作符不需要重载,重载的逗号操作符无法严格从左向右计算逗号表达式,失去了原生逗号操作符的语义。
C、前置操作符与后置操作符
前置操作符和后置操作符支持全局函数、类成员函数重载。
前置的++、--运算操作符可以重载,不需要额外的参数。
后置的++、--运算操作符可以重载,需要一个int类型的占位参数。


#include

using namespace std;

class Test
{
public:
    Test(int i = 0)
    {
        this->i = i;
    }
    //前置操作符++
    Test& operator ++()
    {
        ++i;
        return *this;
    }
    //前置操作符--
    Test& operator --()
    {
        --i;
        return *this;
    }
    //后置操作符--
    Test operator ++(int)
    {
        Test ret(i);
        i++;
        return ret;
    }
    //后置操作符--
    Test operator --(int)
    {
        Test ret(i);
        i--;
        return ret;
    }
    int value()const
    {
        return i;
    }
private:
    int i;
};

int main(int argc, char *argv[])
{
    Test test1(1);
    cout << (++test1).value() << endl;
    Test test2(1);
    cout << (--test2).value() << endl;
    Test test3(1);
    cout << (test3++).value() << endl;
    Test test4(1);
    cout << (test4--).value() << endl;

    return 0;
}


由于类的前置操作符重载函数内部没有额外的临时对象开销,类的前置操作符重载函数效率比后置操作符高。


    int i = 0;
    i++;
    ++i;


对于C++基础类型,前置操作符和后置操作符效率基本相同。
现代C++编译器会对编译代码进行优化,使得编译后的二进制代码更加高效,优化后的二进制代码可能失去C/C++代码的原生语义。


三、类型转换


1、类型转换简介


C++语言中,标准类型之间的转换一般有隐式和显示转换,用户自定义类型间的转换则需要自定义专门的转换函数。
C语言中,基本类型间会进行隐式的类型安全转换,转换规则如下:

 int a = -2000;
 unsigned int b = 1000;
 cout << a + b << endl;//4294966296


上述代码中,int与unsigned int运算时int会被转换为unsigned int,此时a会被转换为unsigned int,是一个非常大的数。


short s = 12;
char c = '1';
cout << sizeof(s + c) << endl;//4


上述代码中,C++编译器会进行优化,编译器遇到short与char进行运算时会将short和char都转换为int,便于高效计算。
C++语言兼容了C语言的隐式类型安全转换。


2、标准类型间转换


基本类型间的转换如下:
A、隐式转换
5.0/8
B、显示转换
(float)5/8


3、C++类类型的转换


C++的类类型之间的转换的规则如下:
A、转换函数定义在源对象类(待转换对象中)中,是转换源的成员函数。
B、一旦为转换源类型提供了到目标类型的转化操作符函数,就可以将源类型对象以隐式转化的方式得的目标类型的对象。
C、应用于构造及初始化,赋值,传参,返回等等场合。


#include

using namespace std;

class Test
{
public:
    Test(int i = 0)
    {
        this->i = i;
        cout << "Test(int i = 0) i = " << i << endl;
    }
private:
    int i;
};

int main(int argc, char *argv[])
{
    Test test;//Test(int i = 0) i = 0
    test = 10;//Test(int i = 0) i = 10

    return 0;
}


上述代码中,编译器会将10使用构造函数Test(int i = 0)隐式转换为Test对象。
实际工程中类的隐    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程