(P24)运算符重载:[]运算符重载 ,+运算符重载 ,+=运算符重载 ,<<运算符重载 ,>>运算符重载

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对象打印输出
    在这里插入图片描述

6. operator<<插入运算符是二元的原因

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

原因:

  • cout的ostream& operator<<(ostream&, const 类类型&)是全局的,就算不在类内使用friend ostream& operator<<(ostream&, const 类类型&);也可以在全局定义这样的一个友元函数;

  • cout的operator<<(),实则是operator<<(ostream,XXXXX)

  • eg:

#include <cstdio>
#include <iostream>


template<typename T>
class AddSpace
{
private:
	T const& ref; // refer to argument passed in constructor
public:
	AddSpace(T const& r): ref(r) {
}

friend std::ostream& operator<< (std::ostream& os, AddSpace<T>s) 
{
	return os << s.ref <<' ' ; // output passed argument and a space
}
};

template<typename... Args>
void print (Args... args) {
( 
	std::cout <<... << AddSpace<Args>(args) ) << '\n' ;
}



int main()
{
  
	print(1,"wang") ; 
  const char arr[10]{2,4,6,8};

    for(const char& c : arr)
    {
      printf("c=%c\n", c);
    }
}

cpp Insights可以看到,模板的实例化结果如下:

#include <cstdio>
#include <iostream>


template<typename T>
class AddSpace
{
  
  private: 
  const T & ref;
  
  public: 
  inline AddSpace(const T & r)
  : ref{r}
  {
  }
  
  friend inline std::basic_ostream<char> & operator<<(std::basic_ostream<char> & os, AddSpace<T> s)
  {
    return (os << s.ref) << ' ';
  }
};

/* First instantiated from: insights.cpp:23 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class AddSpace<int>
{
  
  private: 
  const int & ref;
  
  public: 
  inline AddSpace(const int & r)
  : ref{r}
  {
  }
  
  friend inline std::basic_ostream<char> & operator<<(std::basic_ostream<char> & os, AddSpace<int> s)
  {
    return std::operator<<(os.operator<<(s.ref), ' ');
  }
};

#endif
/* First instantiated from: insights.cpp:23 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class AddSpace<const char *>
{
  
  private: 
  const char *const & ref;
  
  public: 
  inline AddSpace(const char *const & r)
  : ref{r}
  {
  }
  
  friend inline std::basic_ostream<char> & operator<<(std::basic_ostream<char> & os, AddSpace<const char *> s)
  {
    return std::operator<<(std::operator<<(os, s.ref), ' ');
  }
};

#endif


template<typename ... Args>
void print(Args... args)
{
  (std::cout << ... << AddSpace<Args>(args)) << '\n';
}


/* First instantiated from: insights.cpp:31 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void print<int, const char *>(int __args0, const char * __args1)
{
  std::operator<<(operator<<(operator<<(std::cout, AddSpace<int>(__args0)), AddSpace<const char *>(__args1)), '\n');
}
#endif




int main()
{
  print(1, "wang");
  const char arr[10] = {2, 4, 6, 8, '\0', '\0', '\0', '\0', '\0', '\0'};
  {
    const char (&__range1)[10] = arr;
    const char * __begin1 = __range1;
    const char * __end1 = __range1 + 10L;
    for(; __begin1 != __end1; ++__begin1) {
      const char & c = *__begin1;
      printf("c=%c\n", static_cast<int>(c));
    }
    
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值