14.重载运算符

本文详细介绍了C++中运算符重载的概念与实践,包括++运算符、!运算符、=运算符、[]运算符等的重载方式,并通过具体示例展示了如何为自定义类型实现这些运算符的重载。

++运算符重载

前置++运算符重载

成员函数的方式重载,原型为:

  函数类型 & operator++();

友元函数的方式重载,原型为:

  friend 函数类型 & operator++(类类型 &);

后置自增和后置自减的重载

成员函数的方式重载,原型为:

  函数类型 & operator++(int);

友元函数的方式重载,原型为:

   friend函数类型 & operator++(类类型 &,int);

 

	
#ifndef _INTEGER_H_
#define _INTEGER_H_

class Integer
{
public:
    Integer(int n);
    ~Integer();

    Integer &operator++();
    //friend Integer& operator++(Integer& i);

    Integer operator++(int n);
    //friend Integer operator++(Integer& i, int n);

    void Display() const;
private:
    int n_;
};

#endif // _INTEGER_H_
#include "Integer.h"
#include <iostream>
using namespace std;

Integer::Integer(int n) : n_(n)
{
}

Integer::~Integer()
{
}

Integer &Integer::operator ++()
{
    //cout<<"Integer& Integer::operator ++()"<<endl;
    ++n_;
    return *this;
}

//Integer& operator++(Integer& i)
//{
//  //cout<<"Integer& operator++(Integer& i)"<<endl;
//  ++i.n_;
//  return i;
//}

Integer Integer::operator++(int n)
{
    //cout<<"Integer& Integer::operator ++()"<<endl;
    //n_++;
    Integer tmp(n_);
    n_++;
    return tmp;
}

//Integer operator++(Integer& i, int n)
//{
//  Integer tmp(i.n_);
//  i.n_++;
//  return tmp;
//}

void Integer::Display() const
{
    cout << n_ << endl;
}
	
#include "Integer.h"
#include <iostream>
using namespace std;


int main(void)
{
    Integer n(100);
    n.Display();

    Integer n2 = ++n;
    n.Display();
    n2.Display();

    Integer n3 = n++;
    n.Display();
    n3.Display();


    return 0;
}

需要注意的是为了区别于前置++,后置++多了一个int 参数,但实际上是没作用的。而且此时成员函数不能与友元函数共存,因为调用++运算符时不明确。

 

其它运算符重载

!运算符重载

=运算符重载

[]运算符重载

+运算符重载

+=运算符重载

<<运算符重载
>>运算符重载

实现一个String.h

#ifndef _STRING_H_
#define _STRING_H_
#include <iostream>
using namespace std;

class String
{
public:
    String(const char *str = "");
    String(const String &other);
    String &operator=(const String &other);
    String &operator=(const char *str);

    bool operator!() const;
    char &operator[](unsigned int index);
    const char &operator[](unsigned int index) const;

    friend String operator+(const String &s1, const String &s2);
    String &operator+=(const String &other);

    friend ostream &operator<<(ostream &os, const String &str);
    friend istream &operator>>(istream &is, String &str);
    ~String(void);

    void Display() const;
    int Length() const;
    bool IsEmpty() const;

private:
    String &Assign(const char *str);
    char *AllocAndCpy(const char *str);
    char *str_;
};

#endif // _STRING_H_

 


#include "String.h"


String::String(const char *str)
{
    str_ = AllocAndCpy(str);
}

String::String(const String &other)
{
    str_ = AllocAndCpy(other.str_);
}

String &String::operator=(const String &other)
{
    if (this == &other)
        return *this;

    return Assign(other.str_);
}

String &String::operator=(const char *str)
{
    return Assign(str);
}

String &String::Assign(const char *str)
{
    delete[] str_;
    str_ = AllocAndCpy(str);
    return *this;
}

bool String::operator!() const
{
    return strlen(str_) != 0;
}

char &String::operator[](unsigned int index)
{
    //return str_[index];
    //non const 版本调用 const版本

    return const_cast<char &>(static_cast<const String &>(*this)[index]);
}

const char &String::operator[](unsigned int index) const
{
    return str_[index];
}

String::~String()
{
    delete[] str_;
}

char *String::AllocAndCpy(const char *str)
{
    int len = strlen(str) + 1;
    char *newstr = new char[len];
    memset(newstr, 0, len);
    strcpy(newstr, str);

    return newstr;
}

void String::Display() const
{
    cout << str_ << endl;
}

int String::Length() const
{
    return strlen(str_);
}

bool String::IsEmpty() const
{
    return Length() == 0;
}

String operator+(const String &s1, const String &s2)
{
    //int len = strlen(s1.str_) + strlen(s2.str_) + 1;
    //char* newstr = new char[len];
    //memset(newstr, 0, len);
    //strcpy(newstr, s1.str_);
    //strcat(newstr, s2.str_);
    //
    //String tmp(newstr);
    //delete newstr;
    String str = s1;
    str += s2;
    return str;
}

String &String::operator+=(const String &other)
{
    int len = strlen(str_) + strlen(other.str_) + 1;
    char *newstr = new char[len];
    memset(newstr, 0, len);
    strcpy(newstr, str_);
    strcat(newstr, other.str_);

    delete[] str_;

    str_ = newstr;
    return *this;
}

ostream &operator<<(ostream &os, const String &str)
{
    os << str.str_;
    return os;
}

istream &operator>>(istream &is, String &str)
{
    char tmp[1024];
    cin >> tmp;
    str = tmp;
    return is;
}
#include "String.h"
#include <iostream>
using namespace std;

int main(void)
{
    String s1("abcdefg");

    char ch = s1[2];
    cout << ch << endl;

    s1[2] = 'A';
    s1.Display();

    const String s2("xyzabc");
    ch = s2[2];
    //s2[2] = 'M'; Error
    s2.Display();


    String s3 = "xxx";
    String s4 = "yyy";

    String s5 = s3 + s4;
    s5.Display();

    String s6 = "aaa" + s3 + "sdfadfa" + "xxxx";
    s6.Display();

    s3 += s4;
    s3.Display();

    cout << s3 << endl;

    String s7;
    cin >> s7;
    cout << s7 << endl;

if (!s7.IsEmpty())

cout<<s7.Length()<<endl;



    return 0;

}

C++I/O流库的一个重要特性就是能够支持新的数据类型的输出和输入。

用户可以通过对插入符(<<)和提取符(>>)进行重载来支持新的数据类型。

流运算符的重载只能使用友元函数进行重载

问题:为什么一定要使用友元函数进行重载?

friend istream& operator>>(istream&, 类类型&);

friend ostream& operator<<(ostream&, const 类类型&);

只能将流类运算符重载为友元函数,因为第一个参数是流类对象引用,不是String 类。

 

 

 

 

 

 

 

 

 

 

 

<think>好的,我现在需要帮助用户了解运算符重载的规则和限制条件,特别是关于操作数个数、默认参数、结合性和优先级是否可以改变的问题。首先,我需要回顾用户提供的引用内容,确保信息准确。用户引用了四个来源,其中引用[1]详细列出了运算符重载的规则,包括哪些运算符可以重载、不能创建新运算符、函数重载的机制、优先级和结合性不可改变等。引用[2]提到了运算符函数的定义格式,区分了成员函数和友元函数的形式。引用[3]讨论了operator=的返回值类型和拷贝构造函数的影响。引用[4]补充了重载作为类外函数的注意事项。接下来,我需要整理这些信息,回答用户的问题。用户的问题集中在运算符重载的规则及限制,特别是操作数个数、默认参数、结合性和优先级是否可改变。需要确保覆盖这些点,同时引用正确的来源。首先,规则中明确提到(4)重载后的运算符不能改变优先级和结合性,也不能改变操作数的个数和语法结构。同时(7)指出不能有默认参数,否则改变参数个数。因此,这些限制条件需要明确说明。 另外,用户可能对成员函数和友元函数的重载方式有疑问,需要区分两者,比如成员函数隐含this指针,而友元函数可能需要更多参数。引用[2]和[4]提到了这一点。在组织回答时,应该分点列出规则,每个规则后附上对应的引用来源。例如,操作数个数不可变来自引用[1]的规则(4),默认参数不允许来自规则(7)。同时,优先级和结合性不可变同样来自规则(4)。 还需要注意用户提到的系统级指令,如数学表达式使用LaTeX格式,但问题中可能不涉及数学公式,因此需要检查是否需要添加。用户的问题主要是规则描述,可能不需要数学表达式,但如果有示例中的运算符符号,可能需要用$...$包裹,比如operator+。最后,生成相关问题,帮助用户进一步学习,如运算符重载与函数重载的区别,如何选择成员或友元函数形式等。确保问题与主题相关,并引导深入思考。 总结回答结构:1.运算符重载的核心规则:可重载运算符范围、不能新建运算符、函数重载机制。2.操作数相关限制:个数不可变、语法结构不变。3.默认参数禁止。 4.优先级和结合性不可变。 5.其他规则:如必须针对用户自定义类型、不能修改内部类型运算符等。 6.示例说明成员函数与友元函数的区别。 7.相关问题。需要确保每个部分都正确引用来源,如规则(4)对应引用[1],默认参数对应规则(7)等。同时语言要清晰,结构层次分明,符合用户要求的格式。</think>### 运算符重载的规则及限制条件 #### **一、运算符重载的核心规则** 1. **可重载运算符范围** - C++中**大部分运算符**可重载,但以下运算符**禁止重载**[^1]: $$ \text{类属关系运算符} \ (.) \quad \text{成员指针运算符} \ (.*) \quad \text{作用于运算符} \ (::) \quad \text{sizeof} \quad \text{三目运算符} \ (?:) $$ - **不能创建新运算符**(如`@$`),只能重载现有运算符[^1]。 2. **本质是函数重载** 运算符重载通过函数实现,编译器根据参数类型和数量选择重载版本[^1][^3]。 ```cpp // 成员函数形式重载+ Complex operator+(const Complex& other) { return Complex(re + other.re, im + other.im); } ``` --- #### **二、操作数相关限制** 1. **操作数个数不可变** - 重载后的运算符**必须保持原操作数个数**。例如,双目运算符`+`必须有两个操作数[^1][^4]。 - ❌ **错误示例**:将`+`改为单目运算符 ```cpp Complex operator+() { ... } // 错误,改变了操作数个数 ``` 2. **语法结构不可变** - 运算符的语法结构(如中缀形式`a + b`)必须保留,不能修改为函数调用形式[^1]。 --- #### **三、默认参数的限制** - **禁止使用默认参数** 运算符重载函数**不能有默认参数**,否则会改变操作数个数,与规则冲突[^1][^7]。 ```cpp // ❌ 错误示例:为operator+添加默认参数 Complex operator+(const Complex& a, const Complex& b = Complex(0,0)) { ... } ``` --- #### **四、优先级与结合性不可变** - **优先级和结合性固定** 重载后的运算符**优先级**和**结合性**必须与原运算符一致[^1]。例如: $$ a \oplus b \oplus c \quad \text{仍按左结合性计算} $$ --- #### **五、其他重要限制** 1. **仅作用于用户自定义类型** - 运算符重载**不能修改内部类型(如`int`)的运算规则**,只能用于自定义类型或与自定义类型混合使用[^1][^8]。 - ✔️ **合法示例**:`Complex + int` ```cpp Complex operator+(const Complex& c, int x) { ... } ``` 2. **语义一致性要求** - 重载功能应与原运算符语义相似(如`+`实现加法,而非减法)[^1][^6]。 3. **特殊运算符的例外** - `=`和`&`**可自动生成默认实现**,无需显式重载(但建议根据需求自定义)[^1][^9]。 --- #### **六、成员函数与友元函数的选择** | 形式 | 成员函数 | 友元函数 | |---------------|---------------------------------------------|---------------------------------------------| | **隐含参数** | 隐含`this`指针(左操作数) | 需显式声明所有操作数 | | **示例** | `Complex::operator+(const Complex& other)` | `friend Complex operator+(const Complex& a, const Complex& b)` | | **适用场景** | 需访问私有成员且左操作数为当前类对象 | 需支持左操作数为非类类型(如`int + Complex`)[^2][^4] | --- ### **代码示例** ```cpp class Vector { public: // 成员函数形式重载+(左操作数为Vector) Vector operator+(const Vector& other) { return Vector(x + other.x, y + other.y); } // 友元函数形式重载*(支持int * Vector) friend Vector operator*(int scalar, const Vector& v) { return Vector(scalar * v.x, scalar * v.y); } }; ``` --- ### **相关问题** 1. 运算符重载与函数重载在实现机制上有何异同?[^1][^3] 2. 如何通过运算符重载实现自定义类型的隐式类型转换?[^1][^8] 3. 为什么`operator=`需要处理自赋值问题?[^3] 4. 在哪些场景下必须使用友元函数形式重载运算符?[^2][^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值