c++基础学习9-c++中的操作符重载

本文详细介绍了C++中的操作符重载概念及其应用,包括操作符重载的实现方式、成员函数与全局函数的选择依据,以及特殊操作符如[]、=、()和->的重载规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,c++标准库

1,c++标准库并不是c++语言的一部分

2,c++标准库是由c++语言编写而成的类库和函数的集合。

3,c++标准库中定义的类和对象都位于std名称空间中。

4,c++标准库涵盖了c库的功能

5,c++标准库定义了多数常用数据结构,字符串,链表队列,栈等。

c++中的输入输出是由cout << 或 >> endl输入输出的,在c语言中<< 或 >> 只能用于左移右移,那么在c++中为什么会变语义了呢?


2,操作符重载:

操作符重载为操作符提供不用的语义,一个相同函数出在不用的上下文的时候,表现出不用的行为。
c++通过operators关键字利用函数拓展操作符。
operator本质是通过函数重载实现操作符重载。
#include <iostream>
using namespace std;  
struct Complex
{
    int a;
    int b; 
};
Complex operator+ (const Complex& c1, const Complex& c2)
{
    Complex ret;
    ret.a = c1.a + c2.a;
    ret.b = c1.b + c2.b;
    return ret;
}
int main()
{
    Complex c1 = {1,2};
    Complex c2 = {3,4};
    Complex c3 = operator+(c1, c2);
    cout<<"c3.a = " << c3.a << "+j" << c3.b<< endl;
   
    return 0; 
}


 
 
 
那么用operator关键字扩展的操作符可以用于类吗?
class complex{

    private:
        int a;
        int b;
    public:
        int c;

}


 
 
上面的类如果当我们调用其成员a,b就会出错,因为a,b是类的私有成员,在全局函数operator是不能够访问私有成员的,这个时候我们就需要用到下面的了

3 ,c++中类的友元

1,private声明使得类的成员不能被外界访问,但是通过friend关键字可以例外的开放权限

同样 我们可以将上面的操作符重载函数在类中声明为友元
例如:
class Complex
{
    private:
        int a;
        int b;
    public:
        int c;
    friend  Complex operator+ (const Complex& c1, const Complex& c2);
}  


 这样我们就可以使用了。 
操作符重载的本质是通过函数扩展操作符的语义。
操作符重载遵循函数重载的规则。

4,操作符重载下

通过operator关键字能将操作符定义为全局函数。
操作符重载的本质就是函数重载。
问:  
类的成员函数是否可以作为操作符重载的函数?
看一个例子:
#include <cstdlib>
#include <iostream>
using namespace std;
class Complex
{
    private:
        int a;
        int b;
    public: 
    Complex(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    int getA()
    {
        return a;
    } 
    int getB()
    {
        return b;
    }
     friend Complex operator+ (const Complex& c1, const Complex& c2);
    friend ostream& operator<< (ostream& out, const Complex& c);
    friend int main(int argc, char *argv[]);
};
Complex operator+ (const Complex& c1, const Complex& c2)
{
   Complex ret;
    ret.a = c1.a + c2.a;
    ret.b = c1.b + c2.b;
    return ret;
}
ostream& operator<< (ostream& out, const Complex& c)
{
    out<<c.a<<"+"<<c.b<<"i";
    return out; 
}
int main(int argc, char *argv[])
{
    Complex c1(1, 2);
    Complex c2(3, 4);
    Complex c3 = c1 + c2;
    cout<<c1;
    operator<<(cout,c1)<<endl;
    cout<<c2;//<<endl;
    //cout<<c3<<endl;

    return 0; 
}
用成员函数重载的操作符
1,比全局操作符函数少一个参数,左操作数
2,不需要使用friend关键字
所以说,即便我们用成员函数重载,本质上还是函数,c++中的操作符重载,必然是利用函数实现的,不过我们可以选择有的用成员函数来实现,或者全局函数来实现
那么什么时候用全局函数重载操作符,什么时候使用成员函数来重载操作符呢?
1,当无法修改左操作数的类时,使用全局函数进行重载。
(在这个例子中,我们的operator+重载函数的第一个参数就是我们自定义的Complex类,所以我们可以采用全局函数重载,也可以使用成员函数重载。)我们要想通过成员函数来重载ostream中的<<操作符,必须要修改ostream这个类,而左移操作符<<函数中的第一个参数ostream,是c++标准库中的类ostream,这个类不是我们自定义的,无法修改,这时只能声明一个友元,以全局函数的方式重载。
friend ostream& operator<< (ostream& out, const Complex& c);  
2,其他情况可以选择成员函数重载,或者全局函数重载,但是有4个例外。=,[],()和->操作符只能通过成员函数进行重载。
3,=,[],()和->操作符只能通过成员函数进行重载。
赋值,数组运算,函数调用和指针操作符,c++中规定,这四个操作符,能且只能用成员函数进行重载。

重载数组操作符[]

首先有一个疑问,为什么要重载这些操作符?有什么作用?
首先看下面这个例子
//main.c
#include <stdio.h>
#include "Array.h"
int main()
{
    Array a1(10);
    for(int i=0; i<a1.length(); i++)
    {
        a1.setData(i, i);
    }
    for(int i=0; i<a1.length(); i++)
    {
        printf("Element %d: %d\n", i, a1.getData(i));
    }
    Array a2 = a1;
    for(int i=0; i<a2.length(); i++)
    {
    printf("Element %d: %d\n", i, a2.getData(i));
    }

    return 0;
}


 
  
从上面的代码中,很难看出Array是一个数组。因为数组都是这样子来使用的,a[]。
这时,我们就可以通过操作符重载,使得Array支持[]操作符。
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
    private:
        int mLength;
        int* mSpace; 
    public:  
        Array(int length);
        Array(const Array& obj);
        int length();
        ~Array();
        int& operator[](int i);
        Array& operator= (const Array& obj);
        bool operator== (const Array& obj);
        bool operator!= (const Array& obj);
};
#endif

 
  
 
  
这样我们的array就可以当做数组使用了,如下
#include "Array.h"
int main()
{
    Array a1(10);
    Array a2(0);
    Array a3(0);
    if( a1 != a2 )
    {
        printf("a1 != a2\n");
    }
    for(int i=0; i<a1.length(); i++)
    {
        a1[i] = i + 1;
    }
    for(int i=0; i<a1.length(); i++)
    {
        printf("Element %d: %d\n", i, a1[i]);
    }
    a3 = a2 = a1;
    if( a1 == a2 )
    { 
        printf("a1 == a2\n");
    }
    for(int i=0; i<a2.length(); i++)
    {
        printf("Element %d: %d\n", i, a2[i]);
    }

    return 0;
}


 这样,定义了数组对象a1,然后对a1中的元素进行了赋值。单看对象的操作,跟c语言中的数组一模一样,并且,更加强大的是,我们可以通过成员函数 length来得到数组的长度。而c语言中的是原生态数组是无法得到数组长度的。但是c++中利用操作符重载实现的数组就具有很强的特性。 
  
 
  
问:为什么[]重载函数的返回值,是引用类型int&呢?
答:a1[i]相当于一个函数调用:a1.operator[](i),返回的是一个堆空间的元素地址,而这个元素既可以作为左值,又可以作为右值来使用。如果将返回值类型改成int,编译就会出错。之前的课程说过如果要想将函数调用作为左值来使用,返回值类型必须为一个引用类型。否则,只能作为一个右值使用。所以,重载操作符时,返回值类型必须是一个引用类型。
重载赋值操作符=
int main()
{
	Array a1(10);
        Array a2(0);
	for(int i=0; i<a1.length(); i++)
	{
		a1[i] = i; 
	}
	for(int i=0; i<a1.length(); i++)
	{
		printf("Element %d: %d\n", i, a1.getData(i));
	}
	
	a2 = a1;//将a2赋给a1
	for(int i=0; i<a2.length(); i++)
	{
		printf("Element %d: %d\n", i, a2.getData(i));
	}

	return 0;
}


 当运行的时候会出现同一个堆空间被释放了两次 
  
 
  
那么调用拷贝构造函数又如何呢?
int main()
{
	Array a1(10);
	for(int i=0; i<a1.length(); i++)
	{
		a1[i] = i; 
	}
	for(int i=0; i<a1.length(); i++)
	{
		printf("Element %d: %d\n", i, a1.getData(i));
	}
	
        Array a2 = a1;
	
	return 0;
}


 
  
这个时候拷贝构造函数起到了作用,析构时a1,a2释放的是各自的堆空间,但是a2的值是不确定的,因为并没有对其赋值。
在上述的程序中,自定义了拷贝构造函数,避免了两个对象指向同一个堆空间的情况,但是由于没有进行初始化,所以数组对象a2的值是不确定的(注:在声明数组对象时直接初始化,调用的是拷贝构造函数,声明之后在赋值,调用的是重载操作符函数)。

调用重载操作符函数:

c++编译器默认为每个类都提供了一个赋值操作符的重载,默认赋值操作符重载的实现,是成员变量的值的复制。a1赋值给a2后,a1所指向的mspace,直接赋值给了a2的mspace,即a2也指向了a1的mspace,二者指向了相同的堆空间, a2自己的mspace被泄漏掉了。在析构时,就肯定会出现问题。解决方法是:
重载赋值操作符。方法与自定义拷贝构造函数类似。先释放自己的原有空间,然后自己申请一段新的堆空间,这个堆空间大小与a1相同,之后再进行深度赋值操作。在函数返回时,我们要返回的是Array这个对象,而成员函数本身隐含了一个this指针指向其自身,所以,返回*this即可。
 
  
 
  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值