文章目录
前言
提示:这里可以添加本文要记录的大概内容:
提示:以下是本篇文章正文内容,下面案例可供参考
基本概念
运算符函数
//错误:不能为int重新定义内置的运算符
int operator+(int, int);
只能重载已有的运算符,而无权发明新的运算符
可以被重载的运算符
+ - * / % ^
& | ~ ! , =
< > <= >= ++ --
<< >> == != && ||
+= -= /= %= ^= &=
|= *= <<= >>= [] ()
-> ->* new new[] delete delete[]
不能被重载的运算符
:: .* . ? :
直接调用一个重载的运算符函数
//等价调用
data1 + data2;
operator+(data1, data2);
data1 += data2;
data1.operator+=(data2);
某些运算符不应该被重载
重载运算符的求值顺序等属性无法保留
使用与内置类型一致的含义
选择作为成员函数或者非成员
输入和输出运算符
ostream & operator<<(ostream &os, const Sales_data &item){
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
输入输出运算符必须是非成员函数
istream &operator>>(istream &is, Sales_data &item){
double price;
is >> item.bookNo >> item.units_sold >> price;
if (is) //检查输入是否成功
item.revenue = item.units_sold * price;
else //输入失败:对象被赋予默认的状态
item = Sales_data();
return is;
}
输入运算符必须处理输入可能失败的情况,而输出运算符不需要。
istream& operator>>(istream& is, Test&t){
is >> t.a;
if (is)
return is;
else
t.a = 999;
return is;
}
算数和关系运算符
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs){
Sales_data sum = lhs;
sum += rhs;
return sum;
}
相等运算符
bool operator==(const Sales_data &lhs, const Sales_data &rhs){
return lhs.isbn() == rhs.isbn() &&
lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue;
}
bool operator!=(const Slaes_data &lhs, const Sales_data &rhs){
return !(lhs == rhs); //yeah!!!
}
关系运算符
赋值运算符
拷贝赋值和移动赋值运算符
再加个
class StrVec{
public:
StrVec &operator=(std::initializer_list<std::string>);
};
StrVec& StrVec::operator(initializer_list<string> il){
// alloc_n_copy分配内存空间并从给定范围内拷贝元素
auto data = alloc_n_copy(il.begin, il.end());
free(); //销毁对象中的元素并释放内存空间
elements = data.first; //更新数据成员使其指向新空间
first_free = cap = data.second;
return *this;
}
可以重载赋值运算符,不论形参的类型是什么,赋值运算符必须定义为成员函数
复合赋值运算符
与赋值运算符相差无几
下标运算符
class StrVec{
public:
std::string& operator[](std::size_t n){
return elements[n];
}
const std::string& operator[](std::size_t n) const{
return elements[n];
}
//...
};
递增递减运算符
前置递增递减
class StrBlovPtr{
public:
//前置
StrBlobPtr& operator++();
StrBlobPtr& operator--();
//后置, int仅用于标识后置
StrBlobPtr operator++(int);
StrBlobPtr operator--(int);
};
//前置版本:返回递增/递减对象的引用
StrBlobPtr& StrBlobPtr::operator++(){
//如果curr已经指向了容器的尾后位置,则无法递增它
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr& StrBlobPtr::operator--(){
//如果curr是0,则继续递减它将产生一个无效的下标
--curr;
check(curr, "decrement past begin of StrblobPtr");
return *this;
}
//后置版本
StrBlobPtr StrBlobPtr::operator++(int){
//此处无需检查有效性,调用前置递增运算时才需要检查
StrBlobPtr ret = *this; //记录当前值
++*this; //前向移动一个元素,前置++需要检查递增的有效性
return ret; //返回之前记录的状态
}
StrBlobPtr StrBlobPtr::operator--(int){
//此处无需检查有效性,调用前置递减运算时才需要检查
StrBlobPtr ret = *this; //记录当前值
--*this; //前后移动一个元素,前置--需要检查递减的有效性
return ret; //返回之前记录的状态
}
//显示调用
p.operator++(0); //调用后置版本的++
p.operator++( ); //调用前置版本的++
此处int形参并不真正使用,仅用来区分前后置
注意
const StrBlobPtr StrBlobPtr::operator++(int){
StrBlobPtr ret = *this;
++*this;
return ret;
// cosnt 可以防止如下的调用
Test tst(9);
Test a =( tst ++ ++ );//报错
cout<< a << " "<<tst;
成员访问运算符
class StrBlovPtr{
public:
std::string& operator*() const{
auto p = check(curr, "dereference past end")
return (*p)[curr]; //(*p)是对象所指的vector
}
std::string* operator->() const{
//将实际工作委托给解引用运算符
return & this->operator*();
}
};
对箭头返回值的限定
函数调用运算符
struct absInt{
int operator()(int val) cosnt {
return val < 0 ? -val : val;
}
};
int i = -42;
absInt absObj;
int ui = absObj(i); //将i传递给absObj.operator()
函数调用运算符必须是成员函数,可以重载
absObj称为函数对象function object ,“行为像函数一样”
class PrintString{
public:
PrintString(ostream &o = cout, char c = ' '):
os(o), sep(c) {}
void operator()(cosnt string &s) const {os << s << sep;}
private:
ostream *os; //用于写入的目的流
char sep; //用于将不同输出隔开的字符
};
PrintString printer; //使用默认值,打印到cout
printer(s); //在cout中打印s,后面跟一个空格
PrintString errors(cerr, '\n');
errors(s); //在cerr中打印s,后面跟一个换行符
for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));
lambda是函数对象
当编写了一个lambda后,编译器将该表达式翻译成一个未命名类的未命名对象。在lambda表达式产生的类中含有一个重载的函数调用运算符。
stable_sort(wds.begin(), wds.end(),
[](const string &a, const string &b){return a.size() < b.size();} );
//其行为类似于下面这个类的一个未命名对象
class ShorterString{
public:
bool operator()(const string&s1, const string&s2)const
{ return s1.size()<s2.size(); }
};
//用上面的类代器
stable_sort(wds.begin(), wds.end(), ShorterString() );
表示lambda及相应捕获行为的类
//获得第一个指向满足条件元素的迭代器,该元素满足size() is >= sz
auto wc = find_if(wds.begin(), wds.end(),
[sz](cosnt string &a){return a.size() >= sz;} );
//该lambda表达式产生的类将形如:
class SizeComp{
public:
SizeComp(size_t n):sz(n) { } //该形参对应捕获的变量
//该调用运算符的返回类型、形参和函数体都与lambda一致
bool operator()(const string &s)const
{return s.size() >= sz;}
private:
size_t sz;
};
auto wc = find_if(wds.begin(), wds.end(), SizeComp(sz) );
标准库定义的函数对象
<functional.h>头文件中
plus<int> intAdd; //可执行int加法的函数对
negate<int> intNegate; //可对int值取反的函数对
//使用intAdd::operator(int, int)求和
int sum = intAdd(10, 20); //等价于sum=30
sum = intNegate(intAdd(10, 20));
//
sum = intAdd(10, intNegate(10)); //sum=0
在算法中使用标准库函数对象
//降序排序
sort(svec.begin(), svec.end(), greater<string>() );
//greater<string>() 为greater类型的一个未命名的对象
vector<string*> nameTable; //指针的vector
//错误❌:nameTable中的指针彼此之间没有关系,所以<将产生未定义的行为
sort(nameTable.begin(), nameTable.end(),
[](string*a, string*b) {return a<b;} );
//正确:标准库规定指针的less是定义良好的
sort(nameTable.begin(), nameTable.end(), less<string*>() );
关联容器使用less<key_type>对元素排序,因此我们可以定义一个指针的set或者在map中使用指针作为关键值而无需直接声名less
可调用对象与function
不同类型可能具有相同的调用形式
//普通函数
int add(int i, int j){ return i + j; }
//lambda, 其产生一个未命名的函数对象类
auto mod = [](int i, int j){ return i%j; };
//函数对象类
struct divide{
int operator()(int denominator, int divisor)
{ return denominator / divisor;}
};
调用形式:int(int, int)
//构建从运算符到函数指针的映射关系,其中函数接受连个int、返回一个int
map<string, int(*)(int, int)> binops;
//正确:add是一个指向正确类型函数的指针
binops.insert({"+", add});
//错误:mod不是一个函数指针
//mod是个lambda表达式,而每个lambda有它自己的类类型,该类型与binops不符
binops.insert({"%", mod}); //❌
标准库function类型
<functional.h>
function<int(int, int)> f1 = add; //函数指针
function<int(int, int)> f2 = divide(); //函数对象的类
function<int(int, int)> f3 = [](int i, int j) //lambda
{ return i*j;};
cout << f1(4,2) << endl;
cout << f2(4,2) << endl;
cout << f3(4,2) << endl;
map<string, function<int(int, int)>> binops = {
{"+", add},
{"-", std::minus<int>()},
{"*", [](int i,int j){return i*j;}},
{"/", divide()},
{"%", mod}
};
binops["+"](10,5);
//有个问题
int add(int i, int j){return i + j;}
double add(double i, doublej);
binops.insert( {"+", add} ); //错误:哪个add??
//消除二义性
int (*fp)(int, int) = add;
binops.insert({"+", fp});
binops.insert( {"+", [](int a, int b){return add(a, b);} } );
重载、类型转换与运算符
类类型转换class-type conversions
类型转换运算符
类型转换运算符conversion operator是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。
operator type() const;
一个类型转换函数必须是类的成员函数;它不能声名返回类型,形参列表也必须为空。类型转换函数通常应该是const
class SmallInt{
public:
SmallInt(int i=0):val(i)
{ if ( i<0 || i>255) throw std::out_of_range("Bad SmallInt value"); }
operator int() const {return val;}
private:
std::size_t val;
};
SmallInt si;
si = 4; //首先将4隐式的转换成SmallInt,然后调用SmallInt::operator=
si + 3; //首先将si隐式的转换成int, 然后执行整数的加法
//内置类型转换将double实参转换成int
SmallInt si = 3.14; //调用SmallInt(int)构造函数
//SmallInt 的类型转换运算符将si转换成int
si + 3.14; //内置类型转换将所得的int继续转换成double
class SmallInt;
operator int(SmallInt&); //错误,不是成员函数
class SmallInt{}
public:
int operator int() const; //❌指定了返回类型
operator int (int = 0) const; //❌参数列表不为空
operator int*() const {return 42;} //❌42不是一个指针
;
显示的类型转换运算符explicit conversion operator
避免右二义性的类型转换
函数匹配与重载运算符
a sym b
//以下都具有二义性
a.operator\sym(b); //a有一个operatorsym成员函数
operator\sym(a,b); //operatorsym是一个普通函数
总结
调用形式 call signature
类类型转换 conversion operator
类型转换运算符 conversion operator
显式的类型转换运算符 explicit conversion operator
函数对象 function object
函数表 function table
函数模板 function template
重载的运算符 overloaded operator