02 C++中string.h头文件的写法

1、引言

本篇内容主要构造一个string.h的头文件(用于构造一个字符串类String),与01篇内容不同的是该头文件中包含指针数据。因此类的构造中涉及(three big)即拷贝构造copy ctor、拷贝复制copy op、析构函数的用法。

2、string.h的头文件编写

1)头文件使用防卫式声明结构

与01篇类似,头文件构造要使用防卫式声明,如下:

#ifndef __MYSTRING__
#define __MYSTRING__
#include <cstring>

/*
	主要内容部分
*/

#endif //__MYSTRING__
说明:代码中cstring是为了完成下面string类构造而引入的标准库头文件,与防卫式声明无关。

2)在头文件中定义String类结构

class String;  //前置声明

class String 
{
public:
	String(const char * cstr = 0);  //#1
	String(const String& str);		//#2
	String& operator = (const String& str);  //#3
	~String();			//#4
	char * get_c_str() const { return m_data; } //#5
private:
	char * m_data;  //#6
};
注意点:

a、在String类结构中,#6中char * m_data是指向字符串第一个字符地址,是C语言字符串类型的表示方法;

b、#1是构造函数,构造字符串时默认传入为空地址,即空字符;

c、#2是拷贝构造函数,也是构造字符串的一种方法。如Sting s1(s2),即将s2的字符串拷贝构造给s1,传入的是s2字符串的常引用;

d、#3是拷贝赋值方法,是将=右边的字符串拷贝给左边的字符串,如Sting s1 = s2,考虑到有可能用到s1 = s2 = s3的用法,所以返回Sting类型引用;

e、#4是析构函数,用途是释放动态分配的内存;

f、#5是一个简单的内联函数,主要是返回字符串的地址。

3)实现构造函数方法#1

inline 
String::String(const char * cstr)
{
	if (cstr){
		m_data = new char[strlen(cstr) + 1];
		strcpy(m_data, cstr);//深复制
	}
	else{
		m_data = new char[1];
		*m_data = '\0';
	}
}
注意点:

a、因构造函数比较长,所以没有像01篇在类内部实现,而是在外部实现,但函数要加上类声明String::String()的结构;

b、实现方法中考虑传入字符串地址为空和非空两种情况,而字符串是以\0为结束符的,如“hello”用字符表示是:‘h’ 'e' 'l' 'l' 'o' '\0'共6个字符;

c、实现方法中使用了strlen()和strcpy()方法,是头文件cstring中标准库的方法,一个用于返回字符串长度,另一个进行字符串深复制。


3)实现析构函数#4

inline 
String::~String()
{
	delete[] m_data;
}
注意点:

a、释放数据有两种方式即array new 和非array new。该例中m_data是new了一个数组,所以用delete [ ] m_data的形式;而如果m_data不是new一个数组,则用delete m_data形式;

b、创建了析构函数后,声明字符串,使用字符串后会自动释放相应的动态内存;


4)实现拷贝构造函数#2

inline
String::String(const String& str)
{
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}
注意点:

a、拷贝构造函数没有返回值;

b、拷贝构造传入的是本身String类对象或者引用对象;


5)实现拷贝赋值函数#3

inline 
String& String::operator = (const String& str)
{
	if (this == &str)
		return *this;

	delete[] m_data;
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
	return *this;
}
注意点:

a、拷贝复制就是将一个String对象复制给另一个String对象,具体思路是先释放被赋值的对象(delete [ ] m_data),然后被赋值对象创建赋值对象的内存空间大小来放置对象内容,最后用深复制的方法进行拷贝;

b、要考虑到对象自己赋值给自己的情况,即要先判断是否自己赋值自己,若是出现这种情况,则直接返回对象。一方面可以加快代码的运行速度、另一方面避免程序出现bug,因为如果没有这一步操作,释放完被赋值的对象后,赋值对象也就被释放了,程序深复制失败。


6)实现重载<<函数

#include <iostream>
using namespace std;

ostream& operator << (ostream& os, const String& str)
{
	os << str.get_c_str();
	return os;
}
说明:cout使用中原先并没有定义输出String类型结构,所以要进行函数重载,且与01篇类似,该函数的重载要在非成员函数中实现。

3、具体代码实现

1)string.h头文件

#ifndef __MYSTRING__
#define __MYSTRING__
#include <cstring>

class String;  //前置声明

class String 
{
public:
	String(const char * cstr = 0);  //#1
	String(const String& str);		//#2
	String& operator = (const String& str);  //#3
	~String();			//#4
	char * get_c_str() const { return m_data; } //#5
private:
	char * m_data;  //#6
};


inline 
String::String(const char * cstr)
{
	if (cstr){
		m_data = new char[strlen(cstr) + 1];
		strcpy(m_data, cstr);//深复制
	}
	else{
		m_data = new char[1];
		*m_data = '\0';
	}
}

inline 
String::~String()
{
	delete[] m_data;
}

inline
String::String(const String& str)
{
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}

inline 
String& String::operator = (const String& str)
{
	if (this == &str)
		return *this;

	delete[] m_data;
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
	return *this;
}

#include <iostream>
using namespace std;

ostream& operator << (ostream& os, const String& str)
{
	os << str.get_c_str();
	return os;
}
#endif //__MYSTRING__
2)主程序实现对头文件的操作
// string_Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "string.h"
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{	
	String s1("hello");
	String s2("world");
	String s3(s2);
	cout << s3 << endl;

	s3 = s1;
	cout << s3 << endl;
	cout << s2 << endl;
	cout << s1 << endl;
	return 0;
}

/*
	输出:
		world
		hello
		world
		hello
*/

说明:在头文件中引用#include "string.h"



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值