mooc程序设计与算法(三)C++面向对象程序设计 运算符重载

运算符重载的基本概念

有需求:要用来对象之间的运算,比如两个复数的对象的相加:a+b(a.r+b.r、a.v+b.v)

目的:扩展c++中提供的适用范围,可以作用于对象

实质:函数重载。可以写成普通函数的形式和成员函数的形式,使用的时候,就相当于对函数的调用。

运算符可以被多次重载,根据操作数的类型决定调用什么函数。

重载为成员函数时,参数个数为运算符目数减一

重载为普通函数时,参数个数为运算符目数

例子

#include <iostream>
using namespace std;
class Complex{
public:
    double real, imag;
    Complex(double r=0.0, double i=0.0):real(r), imag(i){}
    Complex operator-(const Complex &c);
    void Print();
};
void Complex::Print() {
    cout<<real<<" "<<imag<<endl;
}
Complex operator+(const Complex &a, const Complex &b)
{
    return Complex(a.real+b.real, a.imag+b.imag);
}
Complex Complex::operator-(const Complex &c) {
    return Complex(real-c.real, imag=c.imag);
}
int main(){
    Complex a(1,2);
    Complex c(2,3);
    Complex b = c+a;
    cout<<"b(c+a) is:"<<endl;
    b.Print();
    cout<<"a-c is:"<<endl;
    (a-c).Print();
    return 0;

}
b(c+a) is:
3 5
a-c is:
-1 3

赋值运算符重载

只能重载为成员函数

浅拷贝和深拷贝

直接用等于号,那么两个对象指向同一个地方,如果其中一个对象delete掉,那么另一个对象在消亡的时候,也会delete一次,会导致程序崩溃,而且如果其中一个对象指向另一个地方,那么这个对象就会delete,另一个对象指向的就没有东西了。。。。因此要再写一次等号的重载

考虑返回值,如果返回一个void,那么a=b=c就不行了,如果返回一个String对象的化,在c++中,赋值运算符的返回值是引用,因此,我们要返回那个值的引用

这里还有个问题,就是,复制构造函数的问题了。。。所以要编写一个复制构造函数

上面两个的使用例子:

#include <iostream>
#include <cstring>
using namespace std;
class String{
private:
    char *str;
public:
    String():str(new char[1]){str[0]=0;}
    String(char* s){
        cout<<"    in transform construction "<<endl;
        str = new char[strlen(s)+1];
        strcpy(str, s);
    }
    const char*c_str(){return str;};
    String &operator = (const char *s);
    String &operator = (const String &s);

    String(const String &s){
        cout<<"    in copy construction"<<endl;
        str = new char[strlen(s.str)+1];
        strcpy(str, s.str);
    }
    ~String(){delete[] str;};

};

String& String::operator=(const char *s) {
    cout<<"    in operator = "<<endl;
    delete[]str;
    str = new char[strlen(s)+1];
    strcpy(str, s);
    return *this;
}
String& String::operator=(const String &s) {
    cout<<"    in operator String"<<endl;
    if(this==&s) return *this;
    delete [] str;
    str = new char[strlen(s.str)+1];
    strcpy(str, s.str);
    return *this;
}
void copys(String s){
    cout<<"in copy fun"<<endl;
}
int main()
{
    String s1;
    s1 = "Hello world";//调用赋值语句
    cout<<s1.c_str()<<endl;
    String s2 = "Good luck";//调用初始化语句,类型转换构造函数
    cout<<s2.c_str()<<endl;
    s1 = s1;
    copys(s1);
    cout<<strlen(s1.c_str())<<endl;
    return 0;
}
    in operator = 
Hello world
    in transform construction 
Good luck
    in operator String
    in copy construction
in copy fun
11

运算符重载友元

一般情况下,将运算符重载为类的成员函数比较好,但是有时,成员函数不能满足使用要求,重载为普通函数不能访问类的私有变量,所以就选择将运算符重载为友元

可变长数组类的实现(自己加的pop_back)

#include <iostream>
#include <cstring>
using namespace std;
class CArray{
    int size;//数组元素个数
    int *ptr;//指向动态分配的数组
public:
    CArray(int s=0);//构造函数
    CArray(CArray &a);//复制构造函数
    ~CArray();//析够函数
    void push_back(int v);//添加
    CArray & operator = (const CArray&a);//重载赋值运算符
    int length(){return size;}
    int& operator[](int i);
    void pop_back();
};
int& CArray::operator[](int i) {
    return ptr[i];
}
CArray::CArray(int s):size(s) {
    //构造函数
    if(s==0)
        ptr = NULL;
    else
        ptr = new int[s];
}
CArray::CArray(CArray &a) {
    //拷贝构造函数
    if(!a.ptr){
        //是空的
        ptr = NULL;
        size = 0;
        return ;
    }
    ptr = new int[a.size];
    size = a.size;
    //这里原来是a.ptr;
    memcpy(ptr, a.ptr, sizeof(int)*a.size);
    return ;
}
CArray::~CArray() {
    if(ptr){
        delete[]ptr;
    }
}
CArray& CArray::operator=(const CArray &a) {
    if(ptr == a.ptr) return *this;
    if(a.ptr == NULL){
        if(ptr!=NULL) delete[]ptr;
        ptr=NULL;
        size = 0;
        return *this;
    }
    if(size<a.size){
        if(ptr) delete[]ptr;
        ptr = new int[a.size+1];
    }
    memcpy(ptr, a.ptr, sizeof(int)*a.size);
    size = a.size;
    return *this;
}
void CArray::push_back(int v) {
    if(ptr){
        int*tmpPtr=new int[size+1];
        memcpy(tmpPtr, ptr, sizeof(int)*size);
        delete [] ptr;
        ptr = tmpPtr;//这样就是为了多一个存储空间而已
    }
    else{
        ptr = new int[1];
    }
    ptr[size++] = v;
}
void CArray::pop_back() {
    //删除最后一个元素
    ptr[size-1]=ptr[size];
    size--;
}
int main()
{
    CArray a;
    for (int i = 0; i < 5; ++i) {
        a.push_back(i);
    }
    CArray a2,a3;
    a2 = a;
    for (int i = 0; i < a.length(); ++i) {
        cout<<a2[i]<<" ";
    }
    a2 = a3;
    for (int i = 0; i < a2.length(); ++i) {
        cout<<a2[i]<<endl;
    }
    cout<<endl;
    a[3]=100;
    CArray a4(a);
    for (int i = 0; i < a4.length(); ++i) {
        cout<<a4[i]<<" ";
    }
    cout<<endl;
    a4.pop_back();
    for (int i = 0; i < a4.length(); ++i) {
        cout<<a4[i]<<" ";
    }
    return 0;
}
0 1 2 3 4 
0 1 2 100 4 
0 1 2 100 

流插入运算符和流提取运算符的重载

#include <iostream>
using namespace std;
class CStudent{
public:
    int nAage;
};
ostream&operator<<(ostream & o, const CStudent & s){
    o<<s.nAage;
    return o;
}
int main()
{
    CStudent s;
    s.nAage = 5;
    cout<<s<<"hello";
    return 0;
}
5hello

这里注意,在重载运算符的时候,可以将传入的参数设置为对象的引用,这样可以节省时间和空间的开销,如下例,没有调用复制构造函数:

#include <iostream>
using namespace std;
class people{
private:
    int eyes;
public:
    people(){eyes=2;}
    friend void set_eyes(const  people & p, int i);
    people(const people & p) {
        cout<<"in copy fun"<<endl;
        eyes = p.eyes;
    }
    void printf()const{cout<<eyes;}
};
void set_eyes(const people & p, int i){
    //如果改为const peoplle p就会调用复制构造函数,有多余的时间和空间的开销
    p.printf();
};
int main()
{
    people p;
    set_eyes(p, 3);
    return 0;
}
//输出为:2

类型转换运算符

#include <iostream>
using namespace std;
class Complex{
    double real, imag;
public:
    Complex(double r=0, double i=0):real(r), imag(i){}
    operator double(){return real;}
};
int main()
{
    Complex c(1,2);
    cout<<(double)c<<endl;
    double a = 2+c;//这里相当于double n = 2+c.operator double();
    cout<<a<<endl;
    return 0;
}
//输出:
//1
//3

自增自减运算符

前置作为一元运算符重载

后置作为二元运算符重载

#include <iostream>
using namespace std;
class CDemo{
private:
    int n;
public:
    CDemo(int i=0):n(i){}
    CDemo & operator++();//前置,在原生中,++返回的就是引用
    CDemo operator ++(int);//后置
    operator int(){return n;}
    friend CDemo &operator--(CDemo & );
    friend CDemo operator--(CDemo &, int);
};
CDemo& CDemo::operator++() {
    ++n;
    return *this;
}
CDemo CDemo::operator++(int k) {
    CDemo tmp(*this);
    n++;
    return tmp;
}
CDemo & operator--(CDemo& d){
    d.n--;
    return d;
}
CDemo operator--(CDemo& d, int k)
{
    CDemo tmp(d);
    d.n--;
    return tmp;
}
int main()
{
    CDemo d(5);
    cout<<(d++)<<",";
    cout<<d<<",";
    cout<<(++d)<<",";
    cout<<d<<endl;
    cout<<(d--)<<",";
    cout<<d<<",";
    cout<<(--d)<<",";
    cout<<d<<endl;
    return 0;

}
//5,6,7,7
//7,6,5,5

注意:

1、不允许定义新的运算符

2、符合日常习惯

3、不改变运算符的优先级

4、以下运算符不能被重载:”.”, “.*”, “::”, “?:”, sizeof:

5、重载运算符()、[]、->、=时,必须声明为成员函数

猜你喜欢

转载自blog.csdn.net/abc15766228491/article/details/79936928