C++知识点42——下标运算符[]的重载及string类的实现

一、下标运算符的重载

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[]

因为二者的this形参类型不同,所以可以重载成功

关于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

《C++ Primer》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值