:
string>)
{
autodata=alloc_n_copy(il.begin(),il.end());
free();
elements=data.first;
first_free=cap=data.second;
return*this;
}
赋值运算符必须是成员函数。
复合赋值运算符不非得是类的成员,不过我们还是倾向于把包括复合赋值在内的所有赋值运算都定义在类的内部。
与标准库一致,返回其左侧运算对象的引用
Sales_data&Sales_data:
:
operator+=(constSales_data&rhs)
{
units_sold+=rhs.units_sold;
revenue+=rhs.revenue;
return*this;
}
下标运算符必须是成员函数。
为了与原始定义兼容。
下标返回的是访问元素的引用。
这样做的好处是下标可以出现在赋值运算符的任意一端。
进一步,我们最好定义下标运算的常量版本和非常量版本,当作用一个常量对象时,下标运算符返回常量引用以确保我们不会给返回的对象赋值。
classStrVec{
public:
std:
:
string&operator[](std:
:
size_tn)
{returnelements[n];}
conststd:
:
string&operator[](std:
:
size_tn)const
{returnelements[n];}
private:
std:
:
string*elements;
};
在迭代器类中通常有递增和递减运算符,不必须是成员函数,但因为它们改变的正好是所操作对象的状态,所以建议使用成员函数。
为了与内置版本一致,前置返回的是递增后的对象的引用。
后值饭会的是对象的原值,返回的形式是一个值而非引用。
//前置版本
classStrBlobPtr{
public:
StrBlobPtr&operator++();
StrBlobPtr&operator--();
};
StrBlobPtr&StrBlobPtr:
:
operator++()
{
//如果curr已经指向容器尾后位置,则无法递增它
check(curr,"incrementpastendofStrBlobPtr");
++curr;
return*this;
}
StrBlobPtr&StrBlobPtr:
:
operator--()
{
//如果curr是0,则继续递减它将产生一个无效下标
--curr; //如果curr已经是0,那么我们传递个check的值将是一个表示无效下标的非常大的正整数
check(curr,"decrementpastbeginofStrBlobPtr");
return*this;
}
为了区分前置和后置版本,后置版本接受一个额外的int类型的形参。
只是为了区分前置和后置的函数
classStrBlobPtr{
public:
StrBlobPtroperator++(int);
StrBlobPtroperator--(int);
};
StrBlobPtrStrBlobPtr:
:
operator++()
{
// 此处无效检查有效性,调用前置递增运算时才需要检查。
StrBlobPtrret=*this;
++*this;
returnret;
}
StrBlobPtrStrBlobPtr:
:
operator--(int)
{
StrBlobPtrret=*this;
--*this;
returnret;
}
我们的后置版本是通过前置版本完成工作的。
因为我们不会用到int形参,所以无需为其命名。
显示调用:
StrBlobPtrp(al);
p.operator++(0); ///后置版本
p.operator++();//前置版本
在迭代器类和智能指针类中常常用到解引用运算符(*)和箭头运算符(->)
classStrBlobPtr{
public:
std:
:
string&operator*()const
{
autop=check(curr,"dereferencepastend");
return(*p)[curr];
}
std:
:
string*operator->()const
{
return&this->operator*();
}
};
箭头运算符必须是类的成员。
解引用运算符通常也是类的成员函数,尽管非必须。
返回值分别是非常量string的引用或指针,因为一个StrBlobPtr智能绑定非常量的StrBlob对象(构造函数接受非const)
和大多数其他运算符一样,我们可以令operator*完成我们指定的操作。
但是箭头运算符永远不能丢弃成员范文这个最基本的含义。
我们可以改变箭头从哪个对象当中获取成员,而箭头回去成员这一事实永远不变。
重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象。
如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类。
因为这样的类同时可以存储状态,所以比函数更灵活。
函数调用运算符必须是成原函数。
一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。
如果类定义了调用运算符,则该类的对象称作函数对象。
classPrintString{
public:
PrintString(ostream&o=cout,charc=''):
os(o),sep(c){}
voidoperator()(conststring&s)const{os<
private:
ostream&os;
charsep;
};
函数对象常常用作泛型算法的实参。
for_each(vs.begin(),vs.end(),PrintString(cerr,'\n'));
for_each的第三个参数是类型PrintString的一个临时对象,其中我们用cerr和换行符初始化该对象。
当程序调用for_each时,将会把vs中的没个元素打印到cerr中,元素之间以换行符分隔。
当我们编写了一个lambda表达式后,编译器将该表达式翻译成以个未命名的对象。
lambda表达式中含有一个重载的函数调运运算符。
默认情况下,lambda表达式不能改变他捕获的变量,因此默认情况,由lambda产生的类中的函数调用运算符是以个const成员函数。
如果被声明成可变的,则调用运算符就不是const了
捕获的变量被拷贝到lambda表达式中,因此,这种lambda表达式产生的类必须为每个值捕获变量建立对应的数据成员,同时建立构造函数。
令其使用捕获的变量的值类初始化数据成员。
如:
autowc=find_if(words.begin(),words.end(),[sz](conststring&a){returna.size()>=sz;});
该lambda表达式产生的类是:
classSizeComp{
SizeComp(size_tn):
sz(n){}
booloperator()(conststring&s)const
{returns.size()>=sz;}
private:
size_tsz;
};
autowc=find_if(words.begin(),words.end(),SizeComp(sz));
lambda表达式产生的类不含有默认构造函数、赋值运算符以及默认析构函数;它是否含有默认拷贝/移动构造函数则通常要是捕获的数据类型而定。
标准库定义的函数对象:
都在functional头文件中。
都是模板,应该是plus,下面只给出名字
算术:
plus,minus,multiplies,divides,modulus,negate
关系:
equal_to,not_equal_to,greater,greater_equal,less,less_equal
逻辑:
logical_and,logical_or,logicla_not
在算法中使用标准库的函数对象:
vectornameTable;
sort(nameTable.begin(),nameTable.end(),[](string*a,string*b){returna
sort(nameTable.begin(),nameTable.end(),less());//正确
关联容器使用less对元素排序,因此我们可以定义一个指针的set或者在map中使用指针作为关键值而无需直接声明less
c++的可调用对象:
函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。
和其他对象一样,可调用对象也有类型。
不同的类型可能具有相同的调用形式。
对于几个可调用对象共享同一个调用形式的情况,有时我们会希望把他们看成具有相同的类型。
我们可以使用function来解决这个问题,在functional头文件中。
function的操作:
functionf; f是一个用来存储可调用对象的空function,这些可调用对象的调用形式应与函数类型T相同
functionf(nullptr); 显式构造一个空的function
function f(obj); 在f中存储可调用对象obj的副本。
f 将f作为条件:
当f含有一个可调用对象时为真;否则为假;
f(args) 调用f中的对象,参数是可调用对象对应的参数args
定义为function的成员类型
result_type 该function类型的可调用对象返回的类型
argument_type 当T有一个或两个实参时定义的类型。
如果T只有一个实参,则agreement_type是该类型的同义词;如果有两个实参,则
first_argument_type 和 second_argument_type 分别代表两个实参的类型。
function是一个模板,需要提供额外的信息,function类型能够表示的对象的调用形式。
function
我们声明了一个function类型,它可以表示接受两int、返回以个int的可调用对象。
functionf1=add;///函数指针
functionf2=divide();//函数对象类的对象
functionf3=[](inti,intj){returni*j;};
map>binops={
{"+",add},
{"-",std:
:
minus()},
{"/",divide()},
{"*",[](inti,intj){returni*j;}},
{"%",mod}
};
binops["+"](10,5);
我们不能(直接)将重载函数名字存入function类型的对象中,解决二义性,一种途径是使用函数指针,一种是用lambda表达式
intadd(inti,intj){returni+j;}
Sales_dataadd(constSales_data&,constSales_data&);
map> binops;
binops.insert({"+",add}); //错误:
哪个add?
int(*fp)(int,int)=add;
binops.inset({"+",fp});
binops.insert({"+",[](inta,intb){returnadd(a,b);}});
转换构造函数和类型转换运算符共同定义了类类型转换,这样的转换有时候也被称作用户定义的类类型转换。
负责将一个类类型装换成其他类型。
operatortype()const;
类型转换运算符可以面向任意类型(void除外)进行定义,只要该类型能作为函数的返回类型。
因此,我们不允许转换成数组或者函数,但允许转换成指针(包括数组指针和函数指针)或引用。
类型转换运算符既没有显式的放回类型,也没有形参,而且必须是成员函数。
而且一般是const成员。
classSmallInt{
public:
SmallInt(inti=0):
val(i)
{
if(i<0||i>255)
throwstd:
:
out_of_range("BadSmallIntvalue");
}
operatorint()const{returnval;}
private:
std:
:
size_tval;
};
编译器一次只能执行一个用户定义的类型转换。
但是隐式的用户定义的类型转换可以置于一个标准(内置)类型转换之前或之后,并与其一起使用。
类型转换运算符是隐式执行的。
通常,类很少提供类型转换运算符。
在大多数情况下,如果自动转换,可能会让用户感到很意外。
然而有个例外:
对于类来说,定义向bool的类型转换还是比较普遍的现象。
在早期版本中如果定义一个bool的类型转换。
则它常常遇到一个问题:
inti=42;
cin<
这段代码试图将输出运算符作用于输入流。
因为istream本身没有定义<< 所以本来代码会产生错误。
然而,改代码能使用istream的bool类型装换运算符将cin转换成bool,而这个bool接着会被提升成int并用作内置的左移运算符的左侧运算对象。
显式的类类型转换运算符:
classSmallInt{
public:
explicitoperatorint()const{returnval;}
};
SmallIntsi=3;
si+3; //错误
static_cost(si)+3; //正确
当类型转换运算符是显式的,我们也能执行类型转换,不过必须通过显式的强制类型转换才可以。
但是该规则存在一个例外,当表达式被用作条件,则编译器会将显式的类型转换自动应用于它:
if、while及do语句,for语句头的条件表达式,逻辑运算符的运算对象,条件运算符的条件表达式。
在早起版本,IO定义了想void*的转换规则,以求避免转换过度。
但在新标准,IO这定义的是explicit的转换
向bool的类型转换通常用在条件部分,因此operatorbool一般定义成explicit
如果类中包含一个或多个类型转换,则必须确保在类类型和目的类型之间只存在唯一一种转换方式。
否则将很有可能产生二义性。
通常情况下,不要为类定义相同的类型转换,也不要在类中定义两个及两个以上转换源或转换目标的算术类型的转换。
在一个例子中,我们定义了两中将B转换成A的方法:
以中使用B的类型转换运算符、另一种使用A的以B为参数的构造函数
另外如果定义了一组类型转换,他们的转换源(或转换目标)类型本身可以通过其他类型转换联系在一起,则同样会产生二义性的问题。
最简单也是最困扰我们的例子就是类中定义了多个参数都是算术类型,的构造函数,或目标是。
structA{
A(int=0);
A(double);
operatorint()const;
operatordouble()const;
};
voidf(longdouble);
Aa;
f(a)//;二义性