文章目录

  • ​​1.[]运算符重载​​
  • ​​2.+运算符重载​​
  • ​​3.+=运算符重载​​
  • ​​4.<<运算符重载,插入运算符​​
  • ​​5.>>运算符重载,提取运算符​​

1.[]运算符重载

  • 有const版本和非const版本(nonconst)
    要以非const版本调用const版本,避免代码的重复,不应用const版本调用nonconst版本,因为const版本意味着不能修改对象的数据成员,而nonconst是可以修改的,尽管可以强制转换为nonconst,但是还是不够合理

2.+运算符重载

  • 最好以友元的方式的重载,那么第一个参数可以是字符串,若不用友元的方式,那么第一个参数不可以是字符串

3.+=运算符重载

  • +运算符可以调用+=运算符

4.<<运算符重载,插入运算符

  • 友元方式重载
  • 流运算符重载

5.>>运算符重载,提取运算符

  • 友元方式重载
    (1)C++的I/O流库的一个重要特性就是能够支持新的数据类型的输出和输入。
    (2)用户可以通过对插入符(<<)和提取符(>>)进行重载来支持新的数据类型。
    (3)流运算符的重载只能使用友元函数进行重载
    (4)问题:为什么一定要使用友元函数进行重载?见代码中的解释
friend istream& operator>>(istream&, 类类型&);
friend ostream& operator<<(ostream&, const 类类型&);
  • eg:24cpp\24cpp\24cpp\01.cpp
#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");//会调用:const char& String::operator[](unsigned int index) const
ch = s2[2];
//s2[2] = 'M';//因为s2是const常量,不允许这种操作,所以返回的引用是const引用即可
s2.Display();


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

String s5 = s3 + s4;//这里要返回一个新对象,赋值给s5,不能修改s3和s4对象的值
s5.Display();

//因为“aaa”可以调用String(const char* str="");转换构造成一个字符串类对象
//因为这里是以友元的方式来重载的,而不能用成员函数的方式,成员函数重载隐含的第一个参数是对象自身,而不是字符串
String s6 = "aaa" + s3 + "sdfadfa" + "xxxx";
//String s6 = "xxx" + "aaa" + s3 + "sdfadfa" + "xxxx";//这样不允许,因为没有重载2个char*,所以在做加法时,
//前2个至少有一个是对象
s6.Display();

s3+=s4;
s3.Display();

//插入运算符是二元的,第一个运算符是输出流对象cout,第二个参数是要输出的对象本身s3
//不能以成员函数的方式重载,因为若以成员函数的方式重载,第一个操作会变成对象自身,这明显是不合理的
cout<<s3<<endl;

String s7;
cin>>s7;//从键盘输入
cout<<s7<<endl;



return 0;
}
  • 类声明及定义:
    24cpp\24cpp\24cpp\String.h
#ifndef _STRING_H_
#define _STRING_H_
//#include <iostream>等一般不会包含在头文件当中,而这里使用了ostream类型,则需要包含
#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;

//+运算符是二元运算符,既可以用+运算符来重载,也可以用友元的方式来重载,推荐用友元的方式
//const String& s1用引用的原因是:参数传递过来,会少一次拷贝构造
friend String operator+(const String& s1, const String& s2);

//+=运算符重载,最好以成员函数的方式重载
//返回的时候用引用,避免返回时对象的拷贝
String& operator+=(const String& other);

//输出为string对象
//cout的类型是ostream
//返回的是流对象的引用,可以继续输出
friend ostream& operator<<(ostream& os, const String& str);

//输入到字符串str中
friend istream& operator>>(istream& is, String& str);
~String(void);

void Display() const;

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

#endif // _STRING_H_

24cpp\24cpp\24cpp\String.cpp

#pragma warning(disable:4996)
#include "String.h"
#include <string.h>
//#include <iostream>
//using namespace std;

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];
//为了避免return str_[index];重复,则应该用
//non const 版本调用 const版本

//将非const对象转换为const对象:static_cast<const String&>(*this)
//static_cast<const String&>(*this)[index]就可以调用到const char& String::operator[](unsigned int index) 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;
}

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);//因为返回的是一个对象,所以要返回一个临时对象,如果直接构造一个对象
//返回:return String(newstr);因为构造函数会分配一个空间,这里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_);//other

//要放到自身对象
delete[] str_;

str_ = newstr;
return *this;
}

ostream& operator<<(ostream& os, const String& str)
{
os<<str.str_;//因为内部的<<运算符已经重载好了char*,自定义的类型String是没办法重载的
return os;
}

istream& operator>>(istream& is, String& str)
{
char tmp[1024];
cin>>tmp;
str = tmp;
return is;
}
  • 测试:
    输入5个a到s7对象,再把s7对象打印输出