赞
踩
一、下标运算符的重载
1.概念
如果一个类表示容器,那么要重载下标运算符[],下标运算符必须是成员函数。下表访问运算符通常要有一个const版本和一个非const版本。如果不定义const版本,那么const对象将无法调用下表运算符。非const版本返回对应位置的引用,const版本返回常量对象的引用
2.示例
通过实现一个string类来熟悉下标运算符的重载
string类的整体结构如下
- #include <utility>
- #include <iostream>
- #include <cstring>
-
- using namespace std;
-
- class mystring
- {
- friend istream &operator>>(istream &is, mystring &data);
- friend ostream &operator<<(ostream &os, const mystring &data);
-
- public:
- mystring();
- mystring(const char *str);
- mystring(const mystring &rval);
-
- mystring &operator=(const mystring &rval);
- char &operator[](size_t n);
- const char &operator[](size_t n) const;
-
- size_t size() const;
- const char *c_str() const;
- void swap(mystring &t);
- ~mystring();
-
- private:
- char *data_;
- };
-
- istream &operator>>(istream &is, mystring &data);
- ostream &operator<<(ostream &os, const mystring &data);
具体实现如下和测试
- #define _GLIBCXX_DEBUG
-
- #include "mystring.h"
-
- mystring::mystring():
- data_(new char[1])
- {
- cout<<"mystring()"<<endl;
- data_[0]='\0';
- }
-
- mystring::mystring(const char *str):
- data_(new char[strlen(str)+1])
- {
- cout<<"mystring(const char *str)"<<endl;
- strcpy(data_, str);
- }
-
- mystring::mystring(const mystring &rval):
- data_(new char[rval.size()+1])
- {
- cout<<"mystring(const mystring &t)"<<endl;
- strcpy(data_, rval.c_str());
- }
-
- mystring & mystring::operator=(const mystring &rval)
- {
- cout<<"operator=(const mystring &rval)"<<endl;
- mystring t(rval);
- this->swap(t);
- return *this;
- }
-
- char & mystring::operator[](size_t n)
- {
- cout<<"operator[](size_t n)"<<endl;
- return data_[n];
- }
-
- const char & mystring::operator[](size_t n) const
- {
- cout<<"operator[](size_t n) const"<<endl;
- return data_[n];
- }
-
- size_t mystring::size() const
- {
- cout<<__func__<<endl;
- return strlen(data_);
- }
-
- const char *mystring::c_str() const
- {
- cout<<__func__<<endl;
- return data_;
- }
-
- void mystring::swap(mystring &t)
- {
- cout<<__func__<<endl;
- std::swap(this->data_, t.data_);
- }
-
- mystring::~mystring()
- {
- cout<<"~mystring()"<<endl;
- delete data_[];
- }
-
- istream &operator>>(istream &is, mystring &data)
- {
- cout<<__func__<<endl;
- is>>data.data_;
- return is;
- }
-
- ostream &operator<<(ostream &os, const mystring &data)
- {
- cout<<__func__<<endl;
- os<<data.data_;
- return os;
- }
-
- int main(int argc, char const *argv[])
- {
- mystring t1("1234"), t2("5678");
- mystring t3=t1;
- cout<<t2<<t3<<endl;
- cout<<t2.size()<<endl;
- t1.swap(t2);
- cout<<t1<<t2<<endl;
- mystring t;
- t=t2;
- cout<<t<<endl;
- const mystring t4=t;
- cout<<t[2]<<t4[1]<<endl;
- return 0;
- }
上述代码中,mystring类实现了两个版本的operator[]
- char & mystring::operator[](size_t n)
- {
- cout<<"operator[](size_t n)"<<endl;
- return data_[n];
- }
-
- const char & mystring::operator[](size_t n) const
- {
- cout<<"operator[](size_t n) const"<<endl;
- return data_[n];
- }
都返回的是对应位置的元素的引用。表面看起来,这两个函数的函数名相同,形参列表也相同,好像是违背了函数重载的规则,但是实际上这两个函数的形参列表是不同的
因为operator[]是成员函数,,形参列表中都隐式的存在一个指向创建对象的this指针,非const版本的operator[]的this的类型是mystring *const this,所以,非常量的mystring对象可以调用非常量版本的operator[]
而const版本的operator[]的this的类型是const mystring *const this,所以不仅得永远指向创建对象,而且还要求指向的创建对象不能更改,所以只有const mystring的对象可以调用const版本的operator[]
关于const函数,见博客https://blog.csdn.net/Master_Cui/article/details/106885048
此外,mystring关于operator=的实现使用了swap,而不是将delete原来的对象,然后再重新创建对象
- void mystring::swap(mystring &t)
- {
- cout<<__func__<<endl;
- std::swap(this->data_, t.data_);
- }
-
- mystring & mystring::operator=(const mystring &rval)
- {
- cout<<"operator=(const mystring &rval)"<<endl;
- mystring t(rval);
- this->swap(t);
- return *this;
- }
类不用自定义swap成员,但是如果一个类含有动态分配内存的操作,那么swap可能就是一个优化的手段。上述代码通过自定义swap,只是改变了两个指针的指向对象,而指向的对象并不需要重新分配内存。
减少了动态分配内存和销毁指向对象的次数
参考
https://github.com/chenshuo/recipes/blob/master/string/StringTrivial.h
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。