文章目录
C++primer-学习心得-14-重载运算与类型转换
14.1 基本概念
可以被重载的运算符有:
+ | - | * | / | % | ^ |
& | | | ~ | ! | ` | = |
< | > | <= | >= | ++ | – |
<< | >> | == | != | && | || |
+= | -= | /= | %= | ^= | &= |
|= | *= | <<= | >>= | [] | () |
-> | ->* | new | new[] | delete | delete[] |
不能被重载的运算符:
:: .* . ?:
有两种调用运算符的函数:
data1+data2;
operator+(data1,data2);
这两种调用是等价的。
我们像调用其他成员函数一样显示地调用成员函数运算符函数。
data1+=data2;
data1.operator+=(data2);
有一个必须注意的问题是双目运算符是在类的外部定义,这本来是很合理的,双目运算符其实不能看作类的成员函数,而应是类的友元函数。具体需要注意的一些细节自己实际操作的时候就会注意到了。
练习14.2,14.6,14.9,14.20,14.21,14.22
这个类包含了输入、输出、加法、复合运算(+=),正好是这几个题要求的
#include<string>
#include<iostream>
using namespace std;
class sales_data {
friend istream& operator>>(istream&, sales_data&);
friend ostream& operator<<(ostream&, const sales_data&);
friend sales_data& operator+(sales_data&, sales_data&);
public:
sales_data(string s,unsigned u, double d) :bookid(s), sold(u), avenue(d) {}//构造函数
sales_data():sales_data("", 0, 0.0) {}
string getbookid() { return bookid; }
sales_data(const sales_data& s) :bookid(s.bookid), sold(s.sold),avenue(s.avenue){}//拷贝构造函数
sales_data& operator=(const sales_data& s){ //拷贝赋值运算符
bookid = s.bookid;
sold = s.sold;
avenue =s.avenue;
return *this;
}
sales_data& operator=(const string& str) {
bookid = str;
return *this;
}
~sales_data() = default;//析构函数
//不需要移动操作
sales_data& operator+=( sales_data&);
private:
string bookid;
unsigned sold;
double avenue;
};
istream& operator>>(istream& is, sales_data& s) {
is >> s.bookid >> s.sold >> s.avenue;
return is;
}
ostream& operator<<(ostream& o,const sales_data& s) {
o << s.bookid << "--" << s.sold << "---" << s.avenue;
return o;
}
sales_data& operator+(sales_data& l, sales_data& r) {
if (l.bookid == r.bookid) {
l.sold += r.sold;
l.avenue += r.avenue;
return l;
}
else {
cout << "无效的加法" << endl;
return l;
}
}
sales_data& sales_data::operator+=(sales_data&r) {
return (*this) + r;
}
int main() {
sales_data s1("aaa", 10, 100), s2("aaa", 20, 200), s3;
cin >> s3;
cout << s1 << endl;
cout << s2;
cout << s3;
cout << s1 + s2 << endl;
s2 += s1;
cout << s2 << endl;
s3 = s1 + s2;
s3 = "test";
cout << s3 << endl;
}
14.2 输入和输出运算符
输入运算符必须要处理输入可能失败的情况,而输出运算符不需要。
执行输入运算符时可能发生以下错误:
- 当流含有错误类型的数据时读取操作可能失败。(如需要读入一个int类型结果输入的是string,读取操作及后续的操作也会失败。
- 读取操作到达文件的末尾或者遇到输入流的其他错误时也会失败。
14.3 算术和关系运算符
需要注意的主要是区分双目运算符和单目运算符。
对于关系运算符,如果要用到顺序容器,那么定义operator<会很有必要。
14.4 赋值运算符
14.5下标运算符
下标运算符必须是成员函数。
14.6 递增和递减运算符
这两个运算符都有前置版本和后置版本。
区别为
\\前置
strblobptr& operator++();
strblobptr& operator--();
\\后置
strblobptr& operator++(int);
strblobptr& operator--(int);
为了区分前置和后置,我们为后置提供一个额外的int类型形参,这个形参不会参与运算。
14.7成员访问运算符
这些知识都不难,用起来也非常便利,如下是一个例子
#include<iostream>
#include<memory>
using namespace std;
struct pointer {
int i;
shared_ptr<pointer>p;
~pointer() { p.reset(); }
};
class ptr;
class iters {
friend ptr;
friend bool operator==(iters& l, iters& r);
friend bool operator!=(iters& l, iters& r);
public:
iters() = default;
iters(int i, shared_ptr<pointer>p) :it(&i), pt(p) {}
~iters() { pt.reset(); }
iters& operator++() {
pt = pt->p;
it = &(pt->i);
//cout << "++" << endl;
return *this;
}
iters& operator++(int){
pt = pt->p;
it = &(pt->i);
//cout << "++" << endl;
return *this;
}
int operator*()const {
//cout << "*" << endl;
return *it;
}
private:
int* it;
shared_ptr<pointer>pt;
};
bool operator==(iters& l, iters& r) {
if (l.it == r.it)
return true;
else
return false;
}
bool operator!=(iters& l, iters& r) {
if (l.it == r.it)
return false;
else
return true;
}
class ptr {
public:
ptr() :beg(new pointer) { beg->p = nullptr; }
~ptr() { beg.reset(); }//beg是唯一指向该对象的指针,析构函数会释放对象,释放对象时对象的成员p又会被销毁然后释放对象,
//最终把所有的内存释放了,理论上会这样,但实际上会不会这么做并不清楚。也只有智能指针可以这么做
void insert(int start, int data) {
if (size() == 0 ) {
shared_ptr<pointer>pa(new pointer);
beg->i = data;
beg->p = pa;
pa->p = nullptr;
}
else if (start <= size()) {
shared_ptr<pointer>pp(beg),pa(new pointer);
if (start == 0) {
pa->p = beg->p;
beg -> p = pa;
pa->i = beg->i;
beg->i = data;
}else{
for (int i = 0; i < start-1; i++)
pp = pp->p;
pa->p = pp->p;
pp->p = pa;
pa->i = data;
}
}
}
int at(int order) {
if (order < size()) {
shared_ptr<pointer>pa(beg);
for (int i = 0; i < order; i++)
pa = pa->p;
return pa->i;
}
}
int operator[](int order) { return at(order); }
void push_back(int data) {
insert(size(), data);
}
void push_front(int data) {
insert(0, data);
}
int size() {
int i = 0;
shared_ptr<pointer>pp(beg);
while (pp->p != nullptr) {
i++;
pp = pp->p;
}
return i;
}
void del(int order) {
if (order < size()) {
if (order == 0) {
beg->i = beg->p->i;
beg->p = beg->p->p;//.resize()更好点,似乎
}
else {
shared_ptr<pointer>pa(beg);
for (int i = 0; i < order-1; i++)
pa = pa->p;
pa->p = pa->p->p;
}
}
}
void pop_back() {
del(size() - 1);
}
void pop_front() {
del(0);
}
iters& begin();
iters& end();
private:
shared_ptr<pointer> beg;
iters itt;
};
iters& ptr::begin() {
itt.it=&(beg->i);
itt.pt = beg;
return itt;
}
iters& ptr::end() {
shared_ptr<pointer>pa(beg);
for (int i = 0; i < size(); i++)
pa = pa->p;
itt.it = &(pa->i);
itt.pt = pa;
return itt;
}
int main() {
ptr test;
test.push_back(0);
test.push_back(1);
test.push_back(2);
test.push_front(4);
test.del(1);
test.pop_back();
for (int i = 0; i < test.size(); i++) {
cout << test[i] << endl;
}
test.push_back(111);
cout << *(test.begin()) << endl;
for (auto it = test.begin(); it != test.end(); ++it)
cout << *it << endl;
}
这个程序花费了我很多时间,将我之前的单向链表的包括插入、删除的各种操作包装到一个类。甚至提供了类似迭代器的遍历容器的方式。还是挺有意思的。完全要靠自己去考虑怎么实现这个想法。
14.8 函数调用运算符
一个简单的例子如:
struct absInt{
int operator()(int val)const{
return val<0?-val:val;}
}
int i=-42;
absInt absobj;
int ui=absobj(i);
如果类定了调用运算符,则该类的对象称为函数对象。
练习14.34
#include<iostream>
#include<string>
using namespace std;
struct ite {
int operator()(bool a1, int a2, int a3) {
return a1 ? a2 : a3;
}
};
int main() {
ite test;
int aa = test(true, 3, 5);
cout << aa << endl;
}
练习14.35
#include<iostream>
#include<string>
using namespace std;
class prints {
public:
prints(istream& iss = cin) :is(iss) {}
string operator()() {
string s;
if (getline(is, s))
return s;
else
return " ";
}
private:
istream& is;
};
int main() {
prints test;
string testt = test();
cout << testt << endl;
}
练习14.36
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class prints {
public:
prints(istream& iss = cin) :is(iss) {}
string operator()() {
string s;
if (getline(is, s)) {
strvec.push_back(s);
return s;
}
else
return " ";
}
private:
istream& is;
vector<string>strvec;
};
int main() {
prints test;
string testt = tesdt();
cout << testt << endl;
}
1.lambda是函数对象
stable_sort(words.begin(),words.end(),[](const string&a,const string &b){
return a.size()<b.size();
});
//类似的
class Shorterstring{
public bool operator()(const string &a,const string &b){
return a.size()<b.size();
}
};
有捕获行为的类
auto wc=find_if(words.begin(),words.end(),[sz](const string&a){return a.size()>=sz;});
//类似
class sizecomp{
public:
sizecomp(size_t n):sz(n){}
bool operator()(const string &s)const{
return s.size()>=sz;
private:
size_t sz;
}
}
auto wc=find_if(words.begin(),words.end(),sizecomp(sz));
2.标准库定义的函数对象
标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。