C++ Primer 学习笔记_31_操作符重载与转换(2)--++/--运算符重载、!运算符重载、赋值运算符重载 、String类([]、 +、 += 运算符重载)、>>和<<运算符重载

C++ Primer 学习笔记_31_操作符重载与转换(2)--++/--运算符重载、!运算符重载、赋值运算符重载 、String类([]、 +、 += 运算符重载)、>>和<<运算符重载


一、++/--运算符重载

1、前置++运算符重载
成员函数的方式重载,原型为:

函数类型 & operator++();

友元函数的方式重载,原型为:一般以友元方式重载会多一个参数

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


2、后置++运算符重载为了区分前置和后置,在后置++中多了一个参数int,没有实际作用
成员函数的方式重载,原型为:

函数类型  operator++(int);

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

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


3、前置--运算符重载
成员函数的方式重载,原型为:

函数类型 & operator--();


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

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


4、后置--运算符重载
成员函数的方式重载,原型为:

函数类型  operator--(int);

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

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

5、示例
//Integer.h
#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);
    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_
//Integer.cpp
#include "Integer.h"
#include <iostream>
using namespace std;

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

Integer::~Integer() { }

Integer& Integer::operator++()
{
    ++n_;
    return *this;
}

//Integer& operator++(Integer& i)  //一般以友元方式重载会多一个参数
//{
//  ++i.n_;
//  return i;
//}

Integer Integer::operator++(int n) //要返回临时对象,因此不能返回引用
{
    //后置++有所不同,写法和前置++不同,应该创建一个还未++的临时对象,并返回它
    Integer tmp(n_);
    n_++;
    return tmp;
}

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

Integer& Integer::operator--()
{
    --n_;
    return *this;
}

//Integer& operator--(Integer& i)
//{
//  --i.n_;
//  return i;
//}

Integer Integer::operator--(int n)
{
    //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;
}
//main.cpp
#include "Integer.h"
#include <iostream>
using namespace std;

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

    cout << "++阶段" << endl;
    Integer n2 = ++n;
    n.Display();
    n2.Display();

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

    cout << "--阶段" << endl;
    Integer m(100);
    Integer m2 = --m;
    m.Display();
    m2.Display();

    Integer m3 = m--;
    m.Display();
    m3.Display();
    return 0;
}
运行结果:
100
++阶段
101
101
102
101
--阶段
99
99
98
99

解释:需要注意的是为了区别于前置++,后置++多了一个int 参数,但实际上是没作用的,设置断点调试的时候可以发现默认赋值为0。而且此时成员函数不能与友元函数共存,因为调用++运算符时不明确。



二、赋值运算符重载、!运算符重载

1、示例

//String.h
#ifndef _STRING_H_
#define _STRING_H_
class String
{
public:
     explicit String(const char* str="");  //将转换构造函数加上explicit,没有办法进行转换构造
     String(const String& other);
     String& operator=(const String& other);
     String& operator=(const char* str);
     bool operator!() const;
     ~String(void);
     void Display() const;

private:
     char* AllocAndCpy(const char* str);
     char* str_;
};
#endif // _STRING_H_
//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)  //避免自身赋值给自身,s1 = s1
          return *this;
     delete[] str_;  //调用拷贝构造函数一般还没有str_,所以直接创建。但是调用operator=,已经存在,需要delete
     str_ = AllocAndCpy(other.str_);
     return *this;
}

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

bool String::operator!() const  //字符串非空返回true,字符串空返回false
{
     return strlen(str_) != 0;
}

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;
}
//main.cpp
#include "String.h"
#include <iostream>
using namespace std;

int main(void)
{
     String s1("abc");
     String s2(s1);  //调用拷贝构造函数

     String s3;
     s3 = s1; //调用赋值运算符
     s3.Display();

     s3 = "xxxx"; //由于声明了explicit,不能转换,因此也需要重载operator=(const char* str)
     s3.Display();

     String s4;
     bool notempty;
     notempty = !s4;
     cout << notempty << endl;

     s4 = "aaaa";
     notempty = !s4;
     cout << notempty << endl;
     return 0;
}
运行结果:
abc
xxxx
0
1

解释:假设有三个对象,str1,str2,str3,在程序中语句str1=str2=str3将不能通过编译。!运算符这里指当字符串不为空时为真。



三、String类([]、 +、 += 运算符重载)、>>和<<运算符重载

1、 [] 运算符重载
    推荐用成员函数方式重载

2、+运算符重载
    推荐用友元方式重载

3、 += 运算符重载
    推荐用成员函数方式重载

4、<< 运算符重载
    只能将流类运算符重载为友元函数因为第一个参数是流类引用(cout),不是String 类。
    如果以成员函数重载,第一个参数不能是非String类

5、>> 运算符重载

6、流运算符重载
(1)C++ I/O 流库的一个重要特性就是能够支持新的数据类型的输出和输入。
(2)用户可以通过对插入符( << )和提取符( >> )进行重载来支持新的数据类型。
(3)流运算符的重载只能使用友元函数进行重载
(4)问题:为什么一定要使用友元函数进行重载?
    因为因为第一个参数是流类引用(cout),不是String 类。
(5)friend  istream & operator>>( istream &,  类类型 &);
(6)friend  ostream & operator<<( ostream &,  const   类类型 &);


7、示例

//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;
private:
     String& Assign(const char* str);
     char* AllocAndCpy(const char* str);
     char* str_;
};
#endif // _STRING_H_

//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];
     //应该让non const 版本调用 const版本
     return const_cast<char&>(static_cast<const String&>(*this)[index]);  //const_cast去掉常量性,static_cast转换成为const
}
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);
     //delete newstr;
     //return tmp;

     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;
}
//main.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");
     ch = s2[2];
     //s2[2] = 'M';
     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;
     return 0;
}

运行结果:

c
abAdefg
xyzabc
xxxyyy
aaaxxxsdfadfaxxxx
xxxyyy
xxxyyy
aaaaa
aaaaa


参考:

C++ primer 第四版


你可能感兴趣的:(C++,C++,Primer,操作符重载与转换)