这两天主要学习了14.1-14.7,即重载运算符的操作。大体来说几乎所有运算符都可以重载,不能被重载的运算符有:: .* . ? :
通常情况不重载的运算符有:逗号,取地址,逻辑与和逻辑或。
确定到底是函数实现,还是运算符实现的原则是:只有当操作的含义对于用户来说清晰明了时才可以使用运算符。如果用户对运算符可能有几种不同的理解,则使用这样的运算符将产生二义性。
下面的准则我将有助于我们在将运算符定义为成员函数还是普通的非成员函数做出抉择:
1.赋值,下标,调用和成员访问箭头运算符必须是成员。
2复合赋值运算符一般来说是成员,但非必须
3.改变对象状态的运算符或者与给定类型密切相关的运算符,例如:递增递减和解应用等,通常应该是成员。
4.具有对称性的运算符可能转换为一端的运算对象,如算术,相等性、关系和位运算等,通常为非成员函数。
14.2
输入和输出运算符<< >>必须为非成员函数,其中输入必须检查输入是否成功。并且当输入运算符发生错误时,要负责恢复。
14.3
如果类同时定义了算术运算符和相关的复合赋值运算符,则通常情况应使用复合赋值。
使用==的前提条件是要有一种逻辑相等关系。
如果存在一种可靠的<定义,则应考虑为这个类定义<运算符,且与==对应。
14.4
重载的赋值运算符通常用于实现{}初始化。<initializer_list<xx>>
但今天在对StrBlob使用时发生错误。
14.5
下标运算符必须为成员函数。
如果一个类包含下标成员,则它通常会定义两个版本:一个返回普通引用,另一个是常量成员并返回常量应用
但今天在返回 const char & 时报错,估计是引用的问题。
14.6
递增和递减运算符都要求前置和后置版本,一般是先定义前置,返回引用。后置返回普通类型,并使用前置带来的便利。通过一个无用的int参数来区分前后置。
14.7
箭头运算符必须是函数成员,解引通常也是成员函数。
以下是几个实例
#ifndef STRBLOB_H
#define STRBLOB_H
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>
using namespace::std;
// forward declaration needed for friend declaration in StrBlob
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef std::vector<std::string>::size_type size_type;
// constructors
StrBlob() : data(new std::vector<std::string>()) { }
// pre C++11 no initializer_list, we'll define a constructor
// that takes pointers to an array instead
StrBlob(const std::string*, const std::string*);
string& operator[](size_t);
// size operations
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
// add and remove elements
void push_back(const std::string &t) { data->push_back(t); }
void pop_back();
// element access
std::string& front();
std::string& back();
// interface to StrBlobPtr
StrBlobPtr begin(); // can't be defined until StrBlobPtr is
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string> > data;
// throws msg if data[i] isn't valid
void check(size_type i, const std::string &msg) const;
};
// constructor
inline
StrBlob::StrBlob(const std::string *beg, const std::string *end):
data(new std::vector<std::string>(beg, end)) { }
// StrBlobPtr throws an exception on attempts to access a nonexistent element
class StrBlobPtr {
friend bool eq(const StrBlobPtr&, const StrBlobPtr&);
public:
StrBlobPtr(): curr(0) { }
StrBlobPtr(StrBlob &a, size_t sz = 0): wptr(a.data), curr(sz) { }
std::string& deref() const;
StrBlobPtr& incr(); // prefix version
StrBlobPtr& decr(); // prefix version
StrBlobPtr& operator++();//pre
StrBlobPtr& operator--();//pre
StrBlobPtr operator++(int);
StrBlobPtr operator--(int);
string& operator*() const
{
auto p=check(curr,"deference past end");
return (*p)[curr];
}
string* operator->() const
{
return &this->operator*();
}
private:
// check returns a shared_ptr to the vector if the check succeeds
std::shared_ptr<std::vector<std::string> >
check(std::size_t, const std::string&) const;
// store a weak_ptr, which means the underlying vector might be destroyed
std::weak_ptr<std::vector<std::string> > wptr;
std::size_t curr; // current position within the array
};
inline
std::string& StrBlobPtr::deref() const
{
std::shared_ptr<std::vector<std::string> >
p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
inline
std::shared_ptr<std::vector<std::string> >
StrBlobPtr::check(std::size_t i, const std::string &msg) const
{
// is the vector still around?
std::shared_ptr<std::vector<std::string> > ret = wptr.lock();
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret; // otherwise, return a shared_ptr to the vector
}
// prefix: return a reference to the incremented object
inline
StrBlobPtr& StrBlobPtr::incr()
{
// if curr already points past the end of the container, can't increment it
check(curr, "increment past end of StrBlobPtr");
++curr; // advance the current state
return *this;
}
inline
StrBlobPtr& StrBlobPtr::decr()
{
// if curr is zero, decrementing it will yield an invalid subscript
--curr; // move the current state back one element}
check(-1, "decrement past begin of StrBlobPtr");
return *this;
}
// begin and end members for StrBlob
inline
StrBlobPtr
StrBlob::begin()
{
return StrBlobPtr(*this);
}
inline
StrBlobPtr
StrBlob::end()
{
StrBlobPtr ret = StrBlobPtr(*this, data->size());
return ret;
}
// named equality operators for StrBlobPtr
inline
bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
std::shared_ptr<std::vector<std::string> >
l = lhs.wptr.lock(), r = rhs.wptr.lock();
// if the underlying vector is the same
if (l == r)
// then they're equal if they're both null or
// if they point to the same element
return (!r || lhs.curr == rhs.curr);
else
return false; // if they point to difference vectors, they're not equal
}
inline
bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
return !eq(lhs, rhs);
}
inline
StrBlobPtr& StrBlobPtr::operator++()
{
check(curr,"increment past end of StrBlob");
++curr;
return *this;
}
inline
StrBlobPtr& StrBlobPtr::operator--()
{
--curr;
check(curr,"decrement past begin of StrBlobPtr");
return *this;
}
inline
StrBlobPtr StrBlobPtr::operator++(int)
{
StrBlobPtr ret=*this;
++*this;
return ret;
}
inline
StrBlobPtr StrBlobPtr::operator--(int)
{
StrBlobPtr ret=*this;
--*this;
return ret;
}
#endif
2.
#include <cstring>
using std::strlen;
#include <algorithm>
using std::copy;
#include <cstddef>
using std::size_t;
#include <iostream>
using std::ostream;
#include <utility>
using std::swap;
#include <memory>
using std::uninitialized_copy;
#include "String.h"
// define the static allocator member
std::allocator<char> String::a;
// copy-assignment operator
String & String::operator=(const String &rhs)
{
// copying the right-hand operand before deleting the left handles self-assignment
char *newp = a.allocate(rhs.sz); // copy the underlying string from rhs
uninitialized_copy(rhs.p, rhs.p + rhs.sz, newp);
if (p)
a.deallocate(p, sz); // free the memory used by the left-hand operand
p = newp; // p now points to the newly allocated string
sz = rhs.sz; // update the size
return *this;
}
String& String::operator=(const char *cp)
{
if (p) a.deallocate(p, sz);
p = a.allocate(sz = strlen(cp));
uninitialized_copy(cp, cp + sz, p);
return *this;
}
String& String::operator=(char c)
{
if(p) a.deallocate(p, sz);
p = a.allocate(sz = 1);
*p = c;
return *this;
}
// named functions for operators
ostream &print(ostream &os, const String &s)
{
const char *p = s.begin();
while (p != s.end())
os << *p++ ;
return os;
}
String add(const String &lhs, const String &rhs)
{
String ret;
ret.sz = rhs.size() + lhs.size(); // size of the combined String
ret.p = String::a.allocate(ret.sz); // allocate new space
uninitialized_copy(lhs.begin(), lhs.end(), ret.p); // copy the operands
uninitialized_copy(rhs.begin(), rhs.end(), ret.p + lhs.sz);
return ret; // return a copy of the newly created String
}
// return plural version of word if ctr isn't 1
String make_plural(size_t ctr, const String &word,
const String &ending)
{
return (ctr != 1) ? add(word, ending) : word;
}
// chapter 14 will explain overloaded operators
ostream &operator<<(ostream &os, const String &s)
{
return print(os, s);
}
String operator+(const String &lhs, const String &rhs)
{
return add(lhs, rhs);
}
char& String::operator[](size_t n)
{
auto c=p+n;
return *c;
}
3.
#ifndef STRVEC_H
#define STRVEC_H
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <initializer_list>
#include <deque>
#include <list>
#include <array>
#include <forward_list>
#include <sstream>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
#include <memory>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace::std;
class StrVec {
public:
StrVec() :
elements(nullptr), first_free(nullptr), cap(nullptr) { }
StrVec(initializer_list<string> sl)
{
for (auto i : sl)
push_back(i);
}
StrVec(const StrVec&);
StrVec &operator=(const StrVec&);
StrVec &operator=(initializer_list<string> il)
{
auto temp=alloc_n_copy(il.begin(),il.end());
free();
elements=temp.first;
first_free=cap=temp.second;
return *this;
}
StrVec &operator+=(const StrVec &str)
{
for(auto c=str.begin();c!=str.end();++c)
{
push_back(*c);
}
return *this;
}
bool operator==(const StrVec& rhs)
{
bool judge=true;
if(size()==0)
{
if(rhs.size()!=0)
judge=false;
}
else
{
for(auto i=begin(),j=rhs.begin();i!=end();++i,++j)
{
if(j==rhs.end())
{
judge=false;
break;
}
if(*i!=*j)
{
judge=false;
break;
}
}
}
return judge;
}
bool operator!=(const StrVec& rhs)
{
return !(*this == rhs);
}
bool operator<(const StrVec& rhs)
{
return size()<rhs.size();
}
string& operator[](size_t n)
{
return elements[n];
}
const string& operator[](size_t n) const
{
return elements[n];
}
~StrVec();
void push_back(const string &);
size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }
string* begin() const { return elements; }
string* end() const { return first_free; }
private:
allocator<string> alloc;
void chk_n_alloc() {
if (size() == capacity())
reallocate();
}
pair<string *, string *> alloc_n_copy(const string *, const string *);
void free();
void myfree();
void reallocate();
string* elements;
string* first_free;
string* cap;
};
#endif
#include "StrVec.h"
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
pair<string *, string *> StrVec::alloc_n_copy(const string *b, const string *e)
{
auto data = alloc.allocate(e - b);
return { data,uninitialized_copy(b,e,data) };
}
void StrVec::free()
{
if (elements) {
for (auto p = first_free; p != elements;)
alloc.destroy(--p);
alloc.deallocate(elements, cap - elements);
}
}
void StrVec::myfree()
{
if (elements)
{
for_each(elements, first_free, [this](string &rhs) {alloc.destroy(&rhs); });
alloc.deallocate(elements, cap - elements);
}
}
StrVec::StrVec(const StrVec &s)
{
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec()
{
free();
}
StrVec& StrVec::operator=(const StrVec &rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
cap = first_free = data.second;
return *this;
}
void StrVec::reallocate()
{
auto newcapacity = size() ? 2 * size() : 1;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (auto i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}