C++进阶剖析( 十 六)之字符串类,数组访问操作符,经典问题三

1.1 字符串类
1.1.1C语言字符串VSC++字符串
C语言中的字符串

  • C语言不支持真正意义上的字符串
  • C语言用字符串数组和一组函数实现字符串操作
  • C语言不支持自定义类型,因此无法获得字符串类型

C++中的字符串

  • C++ 直接支持C语言的所有概念
  • C++中没有原生的字符串类型
  • C++ 标准库提供了string类型
    • string 直接支持字符串连接
    • string 直接支持字符串的大小比较
    • string 直接支持子串查找和提取
    • string 直接支持字符串的插入和替换

1.1.2 字符串和数字的转换

  • 标准库中提供了相关的类对字符串和数字进行转换
  • 字符串流类(sstream) 用于string的转换
    • 相关头文件
    • istringstream - 字符串输入流
    • ostringstream - 字符串输出流

1.1.3 使用方法

  • string ===> 数字
istringstream iss("321.12");
double num;
iss>>num;

  • 数字===》字符串
ostringstream oss;
oss <<543.21
string s = oss.str();

1.1.4字符串和数字的转换示例


int main()
{  
	istringstream iss("123.21");
	double num;
	iss >> num;
	cout<< num<<endl;

	ostringstream oss;
	oss <<"543.21";
	string s =oss.str();
	cout<<s<<endl;

	return 0;
}

1.1.5 字符串循环右移

  • 示例
    • “abcdefg 循环右移3位后得到efgabcd”
string right_func(const string&s,unsigned int num)
{
	string ret;
	unsigned int pos;
	num = num %s.length();
	pos= s.length()-num;
	ret = s.substr(pos);
	ret += s.substr(0,pos);
	return ret ;
}

int main()
{
	string s ="abcdefg";
	string s2 = right_func(s,3);
	cout <<s2<<endl;
	return 0;
}

string operator >>(const string&s,unsigned int num)
{
	string ret;
	unsigned int pos;
	num = num %s.length();
	pos= s.length()-num;
	ret = s.substr(pos);
	ret += s.substr(0,pos);
	return ret ;
}

int main()
{
	string s ="abcdefg";
	string s2 =  (s>>4); 
	cout << s2<<endl;	
	return 0;
}

1.1.6 字符串翻转

  • 要求 使用 string 完成
  • 示例 “we;tonight,you” ===⇒ “ew;thginot;uoy”
  • 提示:
    • string 类中提供了成员函数可以查找目标字符的位置

1.2 数组类
1.2.1 类的对象怎样支持数组的下标访问

  • 被忽略的事实
    • 数组访问符是C、C++中的内循环操作符
    • 数组访问符的原生意义是数组访问和指针运算
      a[n] <> *(a+n) <> *(n+a) <=====> n[a];

1.2.2 数组访问操作符([])

  • 只能通过类的成员函数重载
  • 重载函数能且只能使用一个参数
  • 可以定义不同参数的多个重载函数

1.2.3数组类的优化

  • 下面是部分代码
//数组类中的代码
int & operator[](int index)
{
	return m_pointer[index];
}
// 下面这个是常用技巧
IntArray & self()
{
	return *this;	
}
//在C++中能不使用指针就不使用指针
int main()
{
    IntArray* a = IntArray::NewInstance(5);     //这个代码可以通过智能指针类来将指针消除
    
    if( a != NULL )
    {
        IntArray& array = a->self();   //目的是减少指针的使用
        
        cout << "array.length() = " << array.length() << endl;
    
        array[0] = 1;
        
        for(int i=0; i<array.length(); i++)
        {  
            cout << array[i] << endl;
        }
    }
    
    delete a;
    
    return 0;
}

1.3经典问题三之一
1.3.1问题1

  • 什么时候需要重载赋值操作符?
  • 编译器是否提供默认的赋值操作?

1.3.2 答案

  • 编译器为每个类默认重载了赋值操作符
  • 默认的赋值操作符仅仅完成浅copy
  • 当需要进行深copy的时候,必须重载复制操作符
  • 赋值操作符合copy构造函数有相同的存在意义

一般性原则:重载复制操作符,必须要实现深拷贝
1.3.3 代码示例

#include <iostream>
#include <sstream>
#include <string>

using namespace  std;

class Test{
private:
	int m_len;
	char* m_pointer;
public:
	Test(const char * str)
	{
		m_len = strlen(str);
		m_pointer = new char[this->m_len +1];
		strcpy(this->m_pointer,str);
	}
	Test(const Test& obj)
	{	
		this->m_len = obj.m_len;
		this->m_pointer =  new char[this->m_len+1];
		strcpy(this->m_pointer,obj.m_pointer);
	}
	void print()
	{
		printf("m_pointer =%s\n",this->m_pointer);
	}
	~Test()
	{
		delete[] m_pointer;
	}
};

int main()
{  
	Test t1("zhangsan");
	t1.print();
	Test t2  =t1;
	t2.print();
	Test t3("123");
	t3 = t1;
	return 0;
}

上述代码报错了,下面是报错信息
在这里插入图片描述
为啥会这样呢? 原因就是
两个指针指向了同一个内存空间(堆上内存空间),在对象销毁的时候,会调用两次~Test(),结果同一个堆上的内存空间被释放了两次。

  • 结论 : 重载复制操作符,必然需要实现深copy!!!
    -解决方案
class Test{
private:
	int m_len;
	char* m_pointer;
public:
	Test(const char * str)
	{
		m_len = strlen(str);
		m_pointer = new char[this->m_len +1];
		strcpy(this->m_pointer,str);
	}
	Test(const Test& obj)
	{	
		this->m_len = obj.m_len;
		this->m_pointer =  new char[this->m_len+1];
		strcpy(this->m_pointer,obj.m_pointer);
	}
	void print()
	{
		printf("m_pointer =%s\n",this->m_pointer);
	}
	//修改部分
	Test& operator=(const Test&obj)
	{
		if(this->m_pointer != NULL) delete[] m_pointer;
		this->m_len = obj.m_len;
		this->m_pointer = new char[this->m_len + 1];
		strcpy(this->m_pointer,obj.m_pointer);
		return  *this;
	}
	~Test()
	{
		delete[] m_pointer;
	}
};

int main()
{  
	Test t1("zhangsan");
	t1.print();
	printf("before  operator =\n");
	Test t3("123");
	t3.print();
	printf("after  operator =\n");
	t3 = t1;
	t3.print();
	return 0;
}

1.4经典问题三之二
1.4.1问题引出

  • 下面的代码有什么问题
string s = "123456";
const char * p = s.c_str();
cout<<p<<endl;
s.append("abcdefg");
cout<<p<<endl;
  • 结果为啥不是自己期望的。
    在这里插入图片描述

1.4.2 问题分析
在这里插入图片描述
1.4.3 正确做法

  • 正确做法,是用C++的编程思想,而不是C语言和C++ 混合的思想

1.5经典问题三之三
1.5.1 下面代码的输出

#include<iostream>
#include<string>
using namespace std;
int main()
{
	//本质是C++和C语言的思想混合开发,导致结果不是预期的那样
	const char *p  = "123345";  //C
	string s ="";			//C++
	s.reserve(10);			
	for(int i =0;i<5;i++)
	{
		s[i] =p[i];
	}
	if(!s.empty())
	{
		cout <<s <<endl;
	}
	return 0;
}

  • 没打印内容
    在这里插入图片描述
    1.5.2分析
    在这里插入图片描述
    参考一 :狄泰软件学院C++进阶剖析
    参考二 : C++ primer中文版
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值