0.两个要点
a) 一个空类,编译器自动合成默认无参构造函数、析构函数、拷贝构造函数、赋值运算符。
b) 在编写类的时候,必须严格区分对象是否可复制。
1.运算符重载之 string类
1.1 运算符重载的几个要点:
a) 运算符重载可以有成员函数和友元函数的形式,后者比前者多一个参数。
b) =和+=必须重载为成员函数的形式(不太理解原因)。
c) 输入和输出必须为友元函数的形式。而且输入操作符要考虑输入失败的情况。
d) 运算符重载为成员函数的形式,那么该操作符的第一个操作数必然为该类的对象。以+=为例,s += “hello” 那么等价于 s.operator+=(“hello”)。
1.2 String 类源码
#ifndef __STRING_H__ #define __STRING_H__ #include <stddef.h> #include <iostream> #include <string.h> class String{ friend std::ostream &operator<<(std::ostream &, const String &); friend std::istream &operator>>(std::istream &, String &); friend String operator+(const String &, const String &); friend String operator+(const String &, const char *); friend String operator+(const char *, const String &); // friend String &operator+(const char*, const char*); friend bool operator==(const String &, const String &); friend bool operator!=(const String &, const String &); friend bool operator>(const String &, const String &); friend bool operator<(const String &, const String &); friend bool operator>=(const String &, const String &); friend bool operator<=(const String &, const String &); public: String(); String(const char* str); String(const String &other); ~String(); String &operator= (const String &other); String &operator= (const char *str); String &operator+= (const String &other); String &operator+= (const char *str); char &operator[](size_t index); char &operator[](size_t index) const; size_t size() const; void debug() const; private: char *str_; }; inline std::ostream &operator<<(std::ostream &os, const String &s){ return os << s.str_; //将换行权交给调用者 } inline std::istream &operator>>(std::istream &is, String &s){ char buf[1024]; is >> buf; if(is){ //输入失败时不改变原对象 s.str_ = buf; } return is; } inline String operator+(const String &s1, const String &s2){ String ret(s1); //加法不改变原对象 ret += s2; return ret; } inline String operator+(const String &s1, const char *s2){ return s1 + String(s2); } inline String operator+(const char *s1, const String &s2){ return String(s1) + s2; } inline bool operator==(const String &s1, const String &s2){ return ::strcmp(s1.str_, s2.str_) == 0; } inline bool operator!=(const String &s1, const String &s2){ return !(s1 == s2); } inline bool operator>(const String &s1, const String &s2){ return ::strcmp(s1.str_, s2.str_) > 0; } inline bool operator<(const String &s1, const String &s2){ return s2 > s1; } inline bool operator>=(const String &s1, const String &s2){ return !(s1 < s2); } inline bool operator<=(const String &s1, const String &s2){ return !(s1 > s2); } #endif #include "string.h" #include <iostream> #include <string.h> String::String() :str_ (new char[1]) { str_[0] = 0; } String::String(const char* str) :str_(new char[::strlen(str) + 1]) { ::strcpy(str_, str); } String::String(const String &other) :str_(new char[::strlen(other.str_) + 1]) { :: strcpy(str_, other.str_); } String::~String(){ delete[] str_; } String& String::operator=(const String &other){ if(&other != this){ delete[] str_; str_ = new char[::strlen(other.str_) + 1]; ::strcpy(str_, other.str_); } return *this; } String &String::operator=(const char *str){ return operator=(String(str)); } String &String::operator+=(const String &other){ char *ret = new char[size() + other.size() + 1]; ret = str_; ::strcat(ret, other.str_); str_ = ret; return *this; } String &String::operator+=(const char *str){ return operator+=(String(str)); } char &String::operator[](size_t index){ return str_[index]; } char &String::operator[](size_t index) const{ return str_[index]; } size_t String::size() const{ return ::strlen(str_); } void String::debug()const{ std::cout << str_ << std::endl; } #include "string.h" #include <iostream> using namespace std; int main(int argc, const char *argv[]) { String s1("apple"); // cout << s1 << endl; String s3; s3 = "hello"; //cout << "world " + s3 << endl; cout << (s1 == s3) << endl; cout << (s1 > s3) <<endl; cout << (s1 >= s3) << endl; return 0; }
2. 自增运算符的重载
2.1 以整形 Integer 类为例,来重载前缀自增运算符(++i)和 后缀自增运算符(i++),这里要注意的几点:
a) 为了区别两个函数,我们将前缀自增运算符的重载函数中增加一个无用的参数。
b) 重载输出操作符的第二个参数 必须是 const, 因为在调用 i++ 时,函数返回值采用值传递,生成了临时对象,注意临时对象是不能修改的 具有const语义,因而此处必须设为const;对于++i 操作 ,该函数返回当前对象的引用,因而是否为const不影响。因此要记住,重载输出操作符时,第二个参数都设为const 引用,这里第一个参数也必须是引用,因为流对象不能复制和赋值。
c) 在生成临时对象的时候调用了拷贝构造函数。
d) Integer(int data)类型的构造函数,具有一种转化语义,能够将int转化为Integer,而加上explicit就禁用掉了转化语义。加上该关键字后,只能采用原生的构造方式如Integer t(100),而不能使用转化形式Integer t = 33。
e)自增操作符,前置和后置的区别:前置直接修改原对象,直接返回; 后置需要暂存之前的结果,修改对象后,将旧的对象返回。
2.2 源码
#ifndef __INTEGER_H__ #define __INTEGER_H__ #include <iostream> class Integer{ friend std::ostream &operator<<(std::ostream &os, const Integer &itg); public: Integer(); explicit Integer(int data); Integer(const Integer &other); ~Integer(); Integer &operator=(const Integer &i); /* * 为了区别前缀和后缀式 传入一个无用的形参 */ Integer &operator++(); Integer operator++(int); private: int data_; }; inline std::ostream &operator<<(std::ostream &os, const Integer &itg){ return os << itg.data_; } #endif #include "integer.h" Integer::Integer() :data_(0) { } Integer::Integer(int data) :data_(data) { } Integer::Integer(const Integer &other) :data_(other.data_) { std::cout << "call copy construction" << std::endl; } Integer::~Integer() { } Integer &Integer::operator=(const Integer &i){ data_ = i.data_; return *this; } Integer &Integer::operator++(){ std::cout << "call prefix " << std::endl; data_++; return *this; } Integer Integer::operator++(int){//此处的 int无用 因此不用命名 std::cout << "call postfix" << std::endl; Integer ret(*this); data_++; return ret; //这里函数返回时 生成了临时对象; } #include <iostream> #include "integer.h" using namespace std; int main(int argc, const char *argv[]) { Integer itg(10); cout << itg << endl; cout << ++itg << endl; Integer itg2(20); cout << itg2 << endl; cout << itg2++ << endl; /* * 构造函数加上 explicit 之后 * 再使用这种隐身转换就会报错 Integer itg3 = 8; cout << itg3 << endl; */ return 0; }