C++——运算符的重载

目录

1、什么是运算符重载?

2、重载运算符的限制

3、运算符重载函数的总结

4、实现复数的各类运算符重载

(1)通过函数来实现复数相加

(2)通过运算符“+”重载来实现复数相加 

(3)通过运算符 “*” “/” 实现复数乘除

(4)重载“=”

 5、运算符重载返回类型 

(1)运算符重载实现前置++与后置++

(2)值返回与引用返回

用引用作为返回值,省去了拷贝构造和析构,可以避免一些无端的开销 

6、默认拷贝构造函数

有指针作为数据成员不能用默认拷贝

如果调用默认的赋值重载,则让p2的指针重新指向了p1,两指针指向同一块空间,但p2原本的空间仍在

7、模拟string功能 


1、什么是运算符重载?

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

让类类型的对象像基本数据类型一样去操作  例如:可以实现对象+,-* / % == ! = [] () << >>

运算符的重载实际是一种特殊的函数重载,必须定义一个函数,并告诉C++编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数。

定义运算符重载函数的一般格式:

返回值类型 类名::operator重载的运算符(参数表) {……}

operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C++编译器可以将这 类函数识别出来。

2、重载运算符的限制

(1)不可臆造新的运算符.

(2)不能改变运算符原有的优先级、结合性和语法结构,不能改变运算符操作数的个数.

(3)运算符重载不宜使用过多.

(4)重载运算符含义必须清楚,不能有二义性.

(5)C++允许重载的运算符绝大部分运算符可以重载。

不能重载的运算符只有5个:成员访问运算符、成员指针访问运算符(*)、域运算符(::)、长度运算符(sizeof)、条件运算符(?:)

(6)重载运算符的函数不能有默认的参数。

(7)重载的运算符必须和用户定义的类型的对象一起使用,其参数至少应有一个是类的对象(或类对象的引用)。

(8)用于类对象的运算符一般必须重载,但有两个例外,运算符“=”和“&”不必用户重载。

(9)从理论上说,可以将一个运算符重载为执行任意的操作。如,将“>”运算符重载为“<”运算,但这样违背了运算符重载的初衷,非但没有提高可读性,反而使人无法理解程序。应当是重载运算符的功能类似于该运算符作用于标准类型时所能实现的功能。

3、运算符重载函数的总结

1、运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右 操作数作为函数的实参。

2、当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个 或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。

3、单目运算符“++”和“--”存在前置与后置问题。 前置“++”格式为: 返回类型 类名::operator++(){……} 而后置“++”格式为: 返回类型 类名::operator++(int){……} 后置“++”中的参数int仅用作区分,并无实际意义,可以给一个变量名,也可以不给变量名。

4、C++中只有极少数的运算符不允许重载。

C++——运算符的重载_第1张图片

 还有 # , ## , // , / * */

4、实现复数的各类运算符重载

(1)通过函数来实现复数相加

Complex Add(Complex cx) // 第一种写法
{
Complex tmp(Real + cx.Real, Image + cx.Image);
return tmp;
}

// 这里采用Complex对象的引用而不是对象本身,
// 调用时不再重新分配内存建立一个复制的对象,函数效率会更高。
Complex Add(Complex &cx) // 第二种写法
{
Complex tmp(Real + cx.Real, Image + cx.Image);
return tmp;
}

//而在引用形式参数类型说明前加const关键字,表示被引用的实参是不可改变的,
//如程序员不当心在函数体中重新赋值了被引用的实参, C++编译器会认为出错。
Complex Add(const Complex &cx) // 第三种写法
{
Complex tmp(Real + cx.Real, Image + cx.Image);
return tmp;
}

// 成员函数为常方法,如程序员不当心在函数体中对当前对象重新赋值,
// C++编译器会认为出错。
Complex Add(const Complex &cx) const // 第四种写法
{
Complex tmp(Real + cx.Real, Image + cx.Image);
return tmp;
}

// 不要使用引用返回tmp对象,当函数执行结束后,tmp 对象析构了,
//会产生将亡值
Complex & Add(const Complex &cx) const // 第五种写法
{
Complex tmp(Real + cx.Real, Image + cx.Image);
return tmp;
}

(2)通过运算符“+”重载来实现复数相加 

Complex operator+(const Complex& cx) const
{
Complex tmp(Real + cx.Real, Image + cx.Image);
return tmp;
}

(3)通过运算符 “*” “/” 实现复数乘除

//复数乘法: (a+bi) * (c+di) => (a*c - b*d) +(b*c + a*d)i;
//复数除法: (a+bi) /(c+di)
d = c*c + d*d;
x = (a*c + b*d)/d + (b*c - a*d)i;
Complex Complex::operator*(const Complex & c) const
{//重载*
return Complex(Real*c.Real-Image*c.Image,Real*c.Image+c.Real*Image);
}
Complex Complex::operator/(const Complex & c)
{ //重载/
double d = c.Real*c.Real+c.Image*c.Image ;
return Complex((Real*c.Real+Image*c.Image)/d,(Image*c.RealReal*c.Image)/d) ;
}

(4)重载“=”

在类定义中如果没有显式给出赋值运算符重载函数时,并不是不用赋值函数,而是由系统自动调用 缺省的赋值函数。

//赋值语句重载
Complex & operator=(const Complex& cx)
{
	if (this != &cx)
	{
		this->Real = cx.Real;
		this->Image = cx.Image;
	}
	cout << this << "operator=" << &cx << endl;
}
//c1=c2=c3
//c1=c2.operator=(c3)
//c1=operator=(&c2,c3)

 5、运算符重载返回类型 

两种类型:值返回,引用返回

(1)只能作为表达式的右边,则必须值返回  a+b  a-b  a*b  a/b

(2)如果可以作为赋值表达式的左边,建议引用返回  a=b  a[4]  cout<

void main()
{
    int a = 10, b = 3;
    int c = 0;
    c = a + b;//从a内存空间取出a,从b内存空间取出b,进行相加,放到左边c的内存空间中去
    //a+b=c//error,左边为临时空间,不能存放c的值

    ++a = 13;//返回对象本身,未开辟临时空间,将c放到a的存储空间
    cout << a << endl;
    //b++ = 13;//error,存在临时空间

    //前++引用返回,后++值返回
}

(1)运算符重载实现前置++与后置++

//返回的是+1操作之后的值的引用,是一个左值
Int &operator ++()//返回的是引用
{
	value += 1;
	return *this;
}
//返回+1操作之前的值的副本,是一个右值
//看起来先使用后+1,实际上它已经+1了,只不过我们使用的是它+1之前的值
Int operator++(int)//返回的是值
{
	Int tmp = *this;
	++* this;//调用前置++
	return tmp;
}

从代码可以看出,前置++比后置++效率高,不用产生临时对象,不用调用拷贝构造函数

后置++运算符重载,注意需要加int来与前置++重载做区分,语法规定

后置++返回的是值而不是引用,因为不可以返回局部变量

后置++是先将a放到寄存器中,返回的是右值

(2)值返回与引用返回

class A
{
public:
    A(int i=0):m_i(i){}
    void Print()
    {
        cout << m_i << endl;
    }
    A operator+(const A &b)//a.operator+(b)  本质即是实现对象的数据成员的加法
    {
        return m_i + b.m_i;
    }
    A operator-(const A& s)//b.operator-(a)  s接收第二个操作数
    {
        return this->m_i - s.m_i;
    }
    A& operator++()//a.++()
    {
        ++m_i;//将a自己的数据成员进行修改,修改完后返回自己本身
        return *this;
    }
    /*
    * b++,表达式的值是b没有加之前的值,表达式执行完后,b的值变成+1之后的值,所以重载后++时,得体现两种值
    */
    A operator++(int)//c++规定在后++的时候,参数里面写个int,这个int没有实际作用,只是为了区分前++
    {
        //int t = m_i;//先保存以前的值
        //m_i = m_i + 1;
        //return t;//返回没加以前保存的值
        return m_i++;
    }
private:
    int m_i;
};
void main()
{
    A a(2), b(6);
    (a + b).Print();//a.+(b)  +(a,b)
    (b - a).Print();
    (++a).Print();//a.++()
    (b++).Print();//不能调用A operator++(),表达式b++的值是b没加之前的值 6
    b.Print();//b的值是上面加之后的值  7
}

用引用作为返回值,省去了拷贝构造和析构,可以避免一些无端的开销 

class A
{
public:
        A(int i=0):m_i(i){}
        void Print()
        {
            cout << m_i << endl;
        }
        A& operator+(A& t)
        {
            m_i = m_i + t.m_i;//this->m_i=this->m_i+b.m_i  a.m_i=a.m_i+b.m_i
            return *this;//return a
        }
private:
    int m_i;
};
//a+b=c,要如此写,a+b必须有确定的内存单元,可以将结果放到a或者b中去
void main()
{
    A a(7), b(10);
    (a + b).Print();
    ((a + b) = 6).Print();
}

6、默认拷贝构造函数

如果程序员没有提供赋值运算符重载,则类会提供一个默认的,默认的功能为:

class A
{
public:
    A(int i=0,int j=0):m_i(i),m_j(j){}
    void Print()
    {
        cout << m_i << m_j << endl;
    }
private:
    int m_i;
    int m_j;
};
void main()
{
    A a(2, 6);
    A b;
    a.Print();// 2 6
    b.Print();// 0 0
    b = a;//用a的值重新给b赋值,调用了默认的赋值运算符重载函数
    b.Print();
}

如果有指针作为数据成员,则赋值运算符重载也要写出来,让两个不同的对象的指针分别指向内存单元,不过里面的内容保持相同

有指针作为数据成员不能用默认拷贝

如果调用默认的赋值重载,则让p2的指针重新指向了p1,两指针指向同一块空间,但p2原本的空间仍在

class Person
{
public:
    Person(const char *name="\0") 
    {
        m_name = new char[strlen(name) + 1];
        strcpy_s(m_name, strlen(name) + 1, name);
    }
    void Print()
    {
        cout << m_name << endl;
    }
    ~Person()
    {
        delete[]m_name;
        m_name = NULL;
    }
    Person& operator=(const Person& s)
    {
        if (this == &s)//判断自赋值
            return *this;
        delete[]m_name;
        //接着开辟和右边对象s相同大小的内存单元
        m_name = new char[strlen(s.m_name) + 1];
        strcpy_s(m_name, strlen(s.m_name) + 1, s.m_name);
        return *this;
    }
private:
    char* m_name;
};
void main()
{
    Person p1("lisi");
    Person p2;
    p1.Print();//list
    p2.Print();//\0
    p2 = p1;//用p1给p2重新赋值  指向同一空间
    //如果调用默认的赋值重载,则让p2的指针重新指向了p1,两指针指向同一块空间,但p2原本的空间仍在
    p2.Print();
}

7、模拟string功能 

class STR
{
public:
    STR(const char* str = "\0")
    {
        m_str = new char[strlen(str) + 1];
        strcpy_s(m_str, strlen(str) + 1, str);
    }
    STR (const STR& s)
    {
        m_str = new char[strlen(s.m_str) + 1];
        strcpy_s(m_str, strlen(s.m_str) + 1, s.m_str);
    }
    ~STR()
    {
        if (m_str != NULL)
        {
            delete[]m_str;
            m_str = NULL;
        }
    }
    STR operator+(const STR& s)//两个对象中的字符串合并
    {
        char* temp = new char[strlen(m_str) + strlen(s.m_str) + 1];
        strcpy_s(temp, strlen(m_str)+1, m_str);
        strcat_s(temp,strlen(m_str)+strlen(s.m_str)+1, s.m_str);
        STR ss(temp);
        delete[]temp;
        temp = NULL;
        return ss;
       /* STR ss;//有问题
        delete[]ss.m_str;
        ss.m_str= new char[strlen(m_str) + strlen(s.m_str) + 1];
        strcpy_s(ss.m_str, strlen(m_str) + 1, m_str);
        strcat_s(ss.m_str, strlen(m_str) + strlen(s.m_str) + 1, s.m_str);
        return ss;*/
    }
    STR& operator=(const STR& s)
    {
        if (this == &s)
            return *this;
        delete[]m_str;
        m_str = new char[strlen(s.m_str) + 1];
        strcpy_s(m_str, strlen(s.m_str) + 1, s.m_str);
        return *this;
    }
    void Print()
    {
        cout << m_str << endl;
    }
    char& operator[](int index)
    {
        if (index < 0||index >(strlen(m_str)))
            exit(-1);
        return m_str[index];
    }
    bool operator==(const STR& s)
    {
        if (strcmp(m_str, s.m_str) == 0)
        {
            cout << "yes" << endl;
            return true;
        }
        else
        {
            cout << "no" << endl;
            return false;
        }
    }
private:
    char* m_str;
};
void main()
{
    STR a("111"), b("222");
    STR c = a + b;//a+b 拷贝构造
    STR d;
    d = c;//重新给d赋值,运算符重载
    c.Print();
    d.Print();
    c[5] = 'h';//c.[]5
    cout<< c[5] << endl;
    cout <<( a == b )<< endl;
}

你可能感兴趣的:(c++,c++,c语言,开发语言)