c++中 有关自定义string的那些为什么

1、为什么我们要学会写自定义string类

面试官爱考,你有办法吗,没有-.-

2、自定义string类应该如何正确书写

quote一句c++primer中的话:

类的安全性和处理正确性的不够,需要类的设计者(也就是我们)去写拷贝构造和赋值运算符重载函数,而最困难的不是如何书写而是让我们自己本身意识到需要这样做。

关于MyString不得不说的就是:深浅拷贝问题,这个究其原因就是它的成员变量是个char *类型的,如果我们懒到要让编译器自己帮我们去建构造、拷贝构造,赋值运算符重载这些函数,那么问题就是很大滴,因为它也很lazy,它做的操作就是让两个指针指向同一个地方。

举个栗子看:

那么下面就说说如何写一个正确的string:

首先c++中string它是个类对吧

那么我们就写一个类出来(成员函数和成员变量)

class MyString
{
private:
	char *_pData;//对,你没看错,只需要一个char型指针就可以实现哦
public:
	//首先一个类要有构造函数-->保证类的成员变量被正确的初始化
	//第一种写法----正确但不是最优
	MyString(char *pData=NULL)
	{
		if (pData==NULL)
		{
			_pData=new char[1];
			_pData[0]=‘\0‘;
		}
		else
		{
			_pData=new char[strlen(pData)+1];
			strcpy(_pData,pData);
		}
	}
	//第二种写法--比第一种更优:使用初始化列表
	MyString(char *pData=NULL)
		:pData(new char[strlen(pData)+1])
	{
		strcpy(_pData,pData);
	}
	//既然在构造中进行了new那么相对的是不是要在析构中去delete?
	~MyString()
	{
		if (_pData)//这里可以直接不用判断,think about why?
		{
			delete []_pData;
		}
	}
	//拷贝构造-----?为什么需要写,因为成员变量是指针,如果我们不进行自己去写
	//就会出现安全性和正确性的问题,两个指针指向一个空间,当其中一个析构后,
	//另外一个就无法再去访问这片空间,会出现非法操作
	//考点:形参必须传入的是该类型的引用,不然在实参传给形参时
	//就会发生值传递,进行拷贝构造,那么这个拷贝构造就是一个死循环
	//第一种写法
	MyString(const MyString &mstr)
	{
		if (strlen(mstr._pData)==0)
		{
			_pData=new char[1];
			_pData[0]=‘\0‘;
		}
		else
		{
			_pData=new char[strlen(mstr._pData)+1];
			strcpy(_pData,mstr._pData);
		}
	}
	//第二种写法
	MyString(const MyString &mstr)
		:_pData(new char[strlen(mstr._pData)+1])
	{
		strcpy(_pData,mstr._pData);
	}
	//第三种写法:只有在构造和析构的时候开辟和释放空间,内存空间不易出错
	//不会出现MyString实例化对象的错误,考虑到了异常安全性
	MyString(const MyString &mstr)
		:_pData(NULL)
		//_pData没有初始化,随机的空间,如果不赋值为空,会delete失败
	{
		MyString temp(mstr._pData);
		swap(temp._pData,_pData);
	}
	//赋值运算符重载
	//考点:1、返回值是该类型引用(考虑到有连等情况a=b=c)
	//考点:2、形参是const 引用(不会改变形参并且效率高)
	//第一种写法--->缺点:如果在new char[]出错的话,很有可能_pData就变成野指针
	//那么MyString返回的对象就是一个不正确的对象,有异常安全性问题
	MyString& operator=(const MyString &mstr)
	{
		//考点:3、自己给自己赋值的情况,有没有考虑到!
		if (this!=&mstr)
		{
			//考点:4、先释放,一定是释放[]_pData,原因就是构造的方式
			delete []_pData;
			//再开辟
			_pData=new char[strlen(mstr._pData)+1];
			strcpy(_pData,mstr._pData);
		}
		return *this;
	}
	//第二种写法
	MyString &operator=(const MyString &mstr)
	{
		//先开辟
		char *temp=new char[strlen(mstr._pData)+1];
		if (temp==NULL)
		{
			return *this;
		}
		//在释放
		delete []_pData;
		_pData=temp;
		strcpy(_pData,mstr._pData);
		return *this;
	}
	//第三种写法
	MyString &operator=(MyString mstr)
	{
	        swap(mstr._pData,_pData);
		return *this;
	}
	//更优写法
	MyString &operator=(const MyString &mstr)
	{
	    if(&mstr!=this)
	    {
	        MyString temp(mstr._pData);
	        swap(temp._pData,_pData);
	    }
	    return *this;
	}
	//String对象转换成const char*
	const char* C_str()const
	{
		return _pData;
	}
	//求字符串长度
	size_t Size()
	{
		return strlen(_pData);
	}
	//判断是否相等
	bool operator==(const MyString &mstr)const
	{
		if (&mstr!=this)
		{
			if(!strcmp(_pData,mstr._pData))
			{
				return false;
			}
		}
		return true;
	}
	//某个字符
	char operator[](size_t pos)const
	{
		if (pos<strlen(_pData)&&pos>=0)
		{
			return _pData[pos];
		}
		else
		{
			return 0;
		}
	}
	//字符串比较
	int operator<(const MyString &mstr)const
	{
		int truth=strcmp(_pData,mstr._pData);
		if (truth>0)
		{
			return -1;
		}
		else if (truth==0)
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}

};
时间: 2024-12-08 09:57:16

c++中 有关自定义string的那些为什么的相关文章

向集合中存储自定义对象是,自定义对象的设计

自定义对象 通过对List.Set.Map 集合的操作,发现集合的不同,自定义类的定义也有所差异 1.List集合中的自定义对象 由于List底层判断集合是否相同依赖的是equals方法,所以在自定义类时要覆盖equals方法 示例: //自定义类Person class Person{ private String name; private int age; Person(String name, int age){ this.name = name; this.age = age; } p

在JavaScript中生成自定义的对象

使用对象便于组织信息.下面我们介绍如何在JavaScript中生成自定义的对象. ---------------------- JavaScript 对象 在前面几章中我们学到JavaScript中有些内置的对象,比如String, Date, Array等等.除此之外,你还可以定义自己的对象. 对象是一种特殊的数据,含有属性和函数. 下面让我们用一个例子来说明:比如一个人是一个对象.属性是与对象有联系的值,比如人的属性包括姓名,身高,体重,年龄,肤色,眼睛的颜色等等.所有人都有这些属性,但是每

结合手机上网流量业务来说明Hadoop中的自定义数据类型(序列化、反序列化机制)

大家都知道,Hadoop中为Key的数据类型必须实现WritableComparable接口,而Value的数据类型只需要实现Writable接口即可:能做Key的一定可以做Value,能做Value的未必能做Key.但是具体应该怎么应用呢?--本篇文章将结合手机上网流量业务进行分析. 先介绍一下业务场景:统计每个用户的上行流量和,下行流量和,以及总流量和. 本次描述所用数据: 日志格式描述: 日志flowdata.txt中的具体数据: 接下来贴出详细代码,代码中含有详细注释,从代码中可以看出,

Android中View自定义XML属性详解以及R.attr与R.styleable的区别

为View添加自定义XML属性 Android中的各种Widget都提供了很多XML属性,我们可以利用这些XML属性在layout文件中为Widget的属性赋值. 如下所示: <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> 我们可以通过TextView所提供

ListView中使用自定义Adapter及时更xin

在项目中,遇到不能ListView及时更新的问题.写了一个demo,其中也遇到一些问题,一并写出来.好吧,上代码: public class PersonAdapter extends BaseAdapter { private ArrayList<PersonBean> mList; private Context mContext; public PersonAdapter(ArrayList<PersonBean> list, Context context) { mList

Android XML中引用自定义内部类view的四个why

今天碰到了在XML中应用以内部类形式定义的自定义view,结果遇到了一些坑.虽然通过看了一些前辈写的文章解决了这个问题,但是我看到的几篇都没有完整说清楚why,于是决定做这个总结. 使用自定义内部类view的规则 本文主要是总结why,所以先把XML布局文件中引用内部类的自定义view的做法摆出来,有四点: 自定义的类必须是静态类: 使用view作为XML文件中的tag,注意,v是小写字母,小写字母v,小写字母v: 添加class属性,注意,没有带android:命名空间的,表明该自定义view

Android系统在新进程中启动自定义服务过程(startService)的原理分析

在编写Android应用程序时,我们一般将一些计算型的逻辑放在一个独立的进程来处理,这样主进程仍然可以流畅地响应界面事件,提高用户体验.Android系统为我们提供了一个Service类,我们可以实现一个以Service为基类的服务子类,在里面实现自己的计算型逻辑,然后在主进程通过startService函数来启动这个服务.在本文中,将详细分析主进程是如何通过startService函数来在新进程中启动自定义服务的. 在主进程调用startService函数时,会通过Binder进程间通信机制来

在Eclipse中JFrame自定义图标可以显示,但是导出JAR之后无法显示

在我的项目中,一开始我使用这种方式构造图片路径,然后将其设置到JFrame中即可以显示自定义图标 String imagePath = System.getProperty("user.dir") + "/image/icon.png";// 构造图片的路径 Image imageIcon = Toolkit.getDefaultToolkit().getImage(imagePath); jFrame.setIconImage(imageIcon); 项目路径如下

INNO SETUP卸载程序中加入自定义窗体

原文:INNO SETUP卸载程序中加入自定义窗体 [Setup] AppName=My Program AppVerName=My Program v.1.2 DefaultDirName={pf}/My Program [Files] Source: Files/*; DestDir: {app} [Code] const bidDelAll = 1; bidSkipAll = 2; var Form: TSetupForm; CheckListBox: TNewCheckListBox;