C++:03.运算符重载

运算符重载的目的:让对象的运算表现的和内置类型的运算一样。

运算符重载函数,返回值一般不会是void,例如:连续的=就无法用void实现,一般用类类型的引用,可以保证连续运算。

class CComplex   复数类
{
public:
    CComplex(int real = 10, int image = 10):mreal(real), mimage(image)
    {
    	cout << "CComplex(int, int)" << endl;
    }
    ~CComplex(){ cout << "~CComplex()" << endl; }
    CComplex(const CComplex &src):mreal(src.mreal), mimage(src.mimage)
    {
    	cout << "CComplex(const CComplex &src)" << endl;
    }
    CComplex& operator=(const CComplex &src)  只要返回的对象还存在就返回引用
    {
    	cout << "operator=" << endl;
	mreal = src.mreal;
	mimage = src.mimage;
	return *this;
    }
    CComplex operator+(const CComplex &src)   由于返回的是局部变量,出函数析构,所以不能返回引用
    {
	cout << "operator+" << endl;
	CComplex tmp;
	tmp.mreal = this->mreal + src.mreal;
	tmp.mimage = this->mimage + src.mimage;
	return tmp;
    }
private:
    int mreal;
    int mimage;
};

int main()
{
    CComplex comp1;
    CComplex comp2(20, 20);
    CComplex comp3 = CComplex(30, 30);

    调用对象的函数:相当于comp2.operator=(comp3)
    因为返回引用所以很好的解决了连续赋值的问题。
    comp1 = comp2 = comp3;

    cout << "---------------------" << endl;
    
    相当于CComplex  comp1.operator+(comp2)
    CComplex comp4 = comp1 + comp2;

    cout << "---------------------" << endl;
    
    return 0;
}

运算符重载为全局函数时,参数的个数等于运算符的目数(即操作数的个数)。为了使用类里的私有成员所以写成友元函数。

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

编译器寻找对象的成员方法时,如果对象没有相应的运算符重载,就会去全局寻找运算符重载函数。

/*
C++的字符串操作  string类型
*/
class CMyString
{
public:  当然这个类前面构造析构啥的,我没写,仅仅对运算符重载举个栗子

    bool operator>(const CMyString &src)    成员函数 
    首先 > 是个二目运算符,但其左侧对象成为了调用这个函数的对象,所以参数之有一个 
    {
        return strcmp(mpStr, src.mpStr) > 0;
    }

private:
    char *mpStr;

    friend ostream& operator<<(ostream& out, const CMyString& srv);
    friend CMyString operator+(const CMyString&src, const CMyString& srv);
};

ostream& operator<<(ostream& out, const CMyString& srv)   全局函数  
<<这玩意也是个二目的,但右侧是cout不是对象,所以你没办法写再成员函数里
{
    cout << srv.mpStr;
    return out;
}
 
也是全局的  +也是个二目,而且两侧都是对象,为啥不写里面??  
原因:你有可能是CMyString str2 = str1 + "ccc"或CMyString str1 = "ddd" + str2;
首先说前面内个:这个可以调用类里的成员函数,将ccc隐式的转换为类类型,再相加,没毛病。
但对后面这个来说,由于左侧是字符串,不是类类型,所以你只能写成全局函数外。
但你没必要一次写俩+的重载吧,所以为保证这俩都能成功,直接一个全局搞定。
CMyString operator+(const CMyString&src, const CMyString& srv) 
{
    int size = strlen(src.mpStr) + strlen(srv.mpStr) + 1;
    char *p = new char[size];
    strcpy(p, src.mpStr);
    strcat(p, srv.mpStr);
    CMyString tmp(p);
    delete[]p;
    return tmp;
}

int main()
{
    CMyString str1;
    
    一个函数确保了这三个都能实现:
    CMyString str2 = str1 + "ccc";
    CMyString str3 = "ddd" + str1;
    CMyString str4 = str2 + str1;

    if (str5 > str6)
    {
        cout << "str5 > str6" << endl;
    }

    return 0;
}

其他类型  转  类类型,看有没有合适的构造函数
类类型  转  其他类型,提供类型重载函数    
这俩无法共存,因为如果存在编译器就不清楚要调用哪个。

Test t1;
t1 = 20;其他类型  转  类类型   会调用Test(20)显示生成临时对象。


class CInt
{
public:
    CInt(int a = 0) :ma(a){}

    //方法一: 类类型 转换 为整形
    operator int& ()  如果没有&,返回得到就是一个立即数,立即数是无法++的,所以应该返回引用
    {
        return ma;
    }

    //方法二:使用运算符重载,将整形与对象的成员进行操作
    bool operator<(const CInt &src)
    {
	return ma < src.ma;
    }
    void operator++()
    {
        ++ma;
    }
    int& operator[](int *ar)//返回引用,及支持读又能支持写
    {
	return ar[ma];
    }

private:
    int ma;
};

int main()
{
    int array[5] = { 12, 4, 5, 6, 7 };
    CInt i = 0;
    for ( ; i < 5; ++i)
    {
        cout << array[i] << " ";  方法一
	cout << i[array] << " ";  方法二:因为操作符的左侧需要是对象,所以可以这么改
    }
    cout << endl;
    return 0;
}

 如果对象有小阔号()运算符重载函数,称为函数对象,看起来像函数但其实是对象。


 对于++运算符重载:

前置++与后置++是有区别的。

CComplex operator++(int)  // a++ 后置++
{
    return CComplex(this->mreal++, this->mimage++);
    //CComplex temp = *this;逻辑代码,但创建了临时变量,应采用上面的方法。
    //this->mreal++;
    //this->mimage++;
    //return temp;
}

CComplex& operator++()    //++a  前置++
{
	++this->mreal;
	++this->mimage;
	return *this;
}

 由上面可知,使用前置++,效率会更好。


下列运算符不允许重载:
. , .* , :: , ?:,siezof


重载new和delete :

为什么要重载new和delete?

1、出于效率考虑:也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈。

2、堆碎片:分配不同大小的内存可能会在堆上产生很多碎片,以至于很快用完内存。虽然内存可能还有,但是由于都是碎片,也就找不到足够大的内存块满足需要。通过为特定类创建自己的内存分配器,可以确保这种情况不会发生。

编译器看到new时,编译器分配内存并调用构造函数,但是当重载new时,可以改变的只是内存分配部分。

重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。
void* operator new(size_t sz)
{
    printf("operator new: %d Bytes\n",sz);
    void* m = malloc(sz);
    if (!m) 
        puts("out of memory");
    return m;
}
返回值是一个void*,而不是指向任何特定类型的指针。所做的只是分配内存。

参考博文:https://www.cnblogs.com/BaiYiShaoNian/p/4681367.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章