详解C++中实现继承string类的MyString类的步骤_C 语言

昨天师兄又出了道测试题,让我们实现类似于string类的没有MyString类,刚开始很头疼,可是真正在自己写代码的时候又很兴奋的发现,这个过程真的是个很宝贵的机会,让我又有机会可以很好的熟悉回顾C++的很多知识—类设计,构造析构函数,成员函数,友元函数,引用,重载,字符串操作,动态内存分布。。。。。于是昨天花了半天时间写了300多行代码,并认真的进行了相关测试、修改和总结。因为内容有点丰富,所以想分几次写出来,条理也清楚些。

类的空间分配:类给它的每个对象都分配了独立的空间去存储它的数据成员,所有的对象公共的访问类方法进行操作。同时在对象的独立空间中,不包括数据成员动态分配的空间,对象只是记录了动态分配空间的地址(所以在析构函数调用的时候只是删除了对像空间,同时需要用new来删除动态分配的地址)。

一、类声明—mystring.h:
1. 构造函数:
专门用于构建新对象,给成员数据分配必要的内存空间并将值赋给新对象的成员数据。
默认构造函数:
在未提供显式初始化值时,被用来创建对象的构造函数(所以它一般没有参数)

MyString();

复制构造函数:
用于将一个对象复制到新创建的对象中(当然这个被复制的对象必须已经存在)。

MyString(const MyString &str);

给定了一定初始化参数的构造函数:
参数列表中的值会一次赋给新创建对象的各个成员函数:

MyString(const char*str);

2.析构函数:
当对象过期时删除对象所占的内存空间,并且当对象创建时有用New请求的内存空时,在析构函数中同时要调用delete对原来分配的 内存空间进行释放,以防止内存泄露。
~MyString();

3.成员函数:
重载赋值成员函数:

MyString &operator=(const MyString &str);    //利用已有的string对象通过=给一个对象进行赋值
MyString &operator=(const char*str);       //直接用常量字符串进行赋值

一般赋值函数:

MyString &assign(const MyString&str);
MyString &assign(const char*sstr);

几个处理字符串的成员函数:

size_t getsize()const;                  //返回字符串大小
void clear();                      //把字符串清空
bool empty();                       //判断字符串是否为空
void swap(MyString &str);              //交换两个字符串
int compare(const MyString &str)const;      //比较2个字符串的大小
//第一个const说明显式调用的字符串不可更改,括号外面的const说明隐式调用的字符串不可更改,只读数据
int compare(const char*str);

追加函数:

MyString &operator+=(const MyString&str);
MyString &operator+=(const char*str);
MyString &append(const MyString&str);
MyString &append(const char *str);

生成字串:

MyString substr(size_t pos = 0,n=npos) const; 

生成字串,从第0个位置开始长度为n,若N超过长度,则为输出整个字符串的长度

4.友元函数(运算符重载):
友元函数一般都是在类得声明中进行定义,它不属于类得成员函数,但是它和类得成员函数一样同样的可以对类得所有数据成员进行访问。

friend bool operator==(const MyString &str1,const MyString &str2);
friend bool operator==(const char *str,const MyString &str2);
friend bool operator==(const MyString &str1,const MyString *str2);
friend bool operator>(const MyString &str1,const MyString &str2);
friend bool operator>(const char*str1,const MyString &str2);
friend bool operator>(const MyString &str1,const char*str2);

同样还有<等各种比较。

friend MyString operator+(const MyString &str1,const MyString &str2);
friend MyString operator+(const char*str1,const MyString &str2);      //两个字符串进行相加
friend MyString operator+(const MyString &str1,const char*str2);

friend ostream & operator<<(ostream &os,const MyString &str);       //输出命令符的重载

5.成员数据变量:

char *string;                     //指向字符串的指针
int length;                       //字符串的长度
static const int string_number = 0;      //计数创建的字符串的数目

二、实现.cpp文件:
1.构造函数和析构函数:

MyString::MyString()
{
  length = 0;
  string = new char;
  char *s = "/0";
  memcpy(string,s,1);
  ++string_number;
} 

MyString::MyString(const char*str)
{
  length = strlen(str);
  string = new char(length+1);
  memcpy(string,s,length);
  ++string_number;
}
MyString::MyString(MyString &str)
{
  length = str.length;
  string = str.string;
  ++string_number;
} 

MyString::~MyString()
{
  delete[]string;
  --string_number;
}

几个注意的问题:
1)构造函数中必须给所有的数据成员进行初始化。
2)注意在给指向字符串的指针赋值时,左右类型的对应。
char *s代表一个指向字符串的指针,所有右边必须是一个字符串常量“/0”,而不能是‘/0'.
3)一个指针只能指向一个地址,不能同时指向两个。
在给string分配了地址之后,下一步我们肯定是确定分配的地址中存放的具体内容,那么这个时候我们都是使用strcpy()或者是
memcpy()把对应的字符串存入地址中。
如果原来我们成这样实现:

   MyString::MyString()
  {
    length = 0;
    string = new char;
    string = "/0";
    ++string_number;
  }

那么我们在编译和实现的时候都不会发现有什么错,但是析构函数使用delete【】释放内存使执行结果会出现乱码,因为string=“/0”
让它指向了一个字符串,并没有分配内存空间,所以在释放的时候就会出现错误。
4)析构函数中的重要语句 delete【】不要忘
析构函数在使用的时候只会释放为对象分配的空间,但是对象的空间中只是存储了数据成员分配内存的地址,所以并没有释放数据成员
的内存空间,必须使用delete[]来进行释放,防止内存泄露 
2.重载运算符的成员函数:

MyString &MyString::operator+=(const MyString&str)
{
   char *dest;
   dest = new char[str.length+length+1];
   memcpy(dest,string,length);
   memcpy(dest+length,str.string,str.length+1);
   delete[]string;
   length = length+str.length;
   string = dest;
   return*this;
}
MyString &MyString::operator+=(const char*str)
{
   char *dest;
   dest = new char[strlen(str)+length+1];
   memcpy(dest,string,length);
   memcpy(dest+length,str,strlen(str)+1);
   delete[]string;
   string = dest;
   return *this;
} 

 //字符串赋值
MyString &MyString::operator=(const MyString&str)
{
  if(&str == this)
    return *this;
  delete[]string;
  string = new char[str.length];
  memcpy(string,str.string,str.length);
  length = str.length;
  return *this;
}

 
注意的幾個問題:
1)+=运算中,调用函数的对象最终字符串长度肯定大于其原来的长度,所以在这个函数调用过程中,我们必须给字符串重新分配一块
两个字符串长度和大小的内存区域。但是因为函数返回值又必须是原调用对象的引用,所以我们要定义一个中间变量指针来存储2个
字符串合并结果,最后释放string指针原来的内存区域后,再让它指向合并字符串。
2)“=”赋值运算肯定和构造函数的初始化不一样。
在使用这个方法以前,那么对象肯定至少已经通过调用构造函数数据成员有了一定的值。所以这个时候我们首先判断两个对象是否
相等。相等的话返回原对象,如果不等:
那么两个对象的字符串长度肯定不同。所以我们先释放原字符串内存空间,然后根据赋值对象的字符串长度分配内存空间并把字符
串内存拷贝过去。
3.字符串处理的几个函数:

size_t MyString::getsize(MyString &str)
{
 return strlen(str.string);
}
void MyString::clear()
{
 length = 0;
 while(string!='/0')
 *string ='/0';
}
bool MyString::empty()
{
 return strlen(string)==0;
}
int MyString ::compare(const MyString &str)
{
  return compare(string,str.string);
}
void MyString::swap(MyString &str)
{
  char *temp;
  temp = string;
  string = str.string;
  str = temp;
}
Mystring MyString::substr(sizez_t pos=0,size_t n )const
{
  MyString string;
  delete[]string.string;
  if(n>length)
  {
   string.length = length;
   string.string = new char[length+1];
   memcpy(string.string,string,length+1);
    return string;
  }
  length = n;
  string.string = new char[length+1];
  memcpy(string.string,string,length+1);
  return string;
}

注意的几个问题:
1)在这几个函数的实现中,我们可以直接调用c语言中几个对字符串处理的<string.h>函数进行实现
2)clear()函数中注意,只是把每个内存区域的字符置为0,并不能通过delete[]来释放内存空间,这样很容易和析构函数一起造成
两次释放内存空间引起错误。
3)swap()实现交换的这个函数中,我们可以直接定义中间变量指针实现,不用重新分配内存空间全部进行转存,这个对于析构函数
的析构没有影响。
4.友元函数:

friend bool operator==(const MyString &str1,const MyString &str2) 

  return strcmp(str1.string,str2.string)==0; 

friend MyString operator+(const MyString &str1,const MyString &str2)
{
  MyString mystring;
  char *dest;
  dest = new char[str1.length+str2.length+1];
  memcpy(dest,str1.string,str1.length);
  memcpy(dest+str1.length,str2.string,str2.length+1);
  delete[]mystring.string;
  mystring.string = dest;
  mystring.length = str1.length+str2.length;
  return mystring;
}
friend ostream &operator<<(ostream &os,const MyString &str)
{
  os<<str.string;
  return os;
}

注意的问题和上面差不多,这里就不重复了~~~
其他几个函数实现都基本雷同~

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
, string
, 字符串
MyString
lr eval string详解、是否可以继承string类、spannablestring 详解、string 方法详解 c、string类可以被继承吗,以便于您获取更多的相关知识。

时间: 2024-08-07 01:51:44

详解C++中实现继承string类的MyString类的步骤_C 语言的相关文章

详解.NET中的加密算法总结(自定义加密Helper类续)_实用技巧

1.1.1 摘要 相信许多人都使用过.NET提供的加密算法,而且在使用的过程我们必须了解每种加密算法的特点(对称或非对称,密钥长度和初始化向量等等).我也看到过很多人写过.NET中加密算法总结,但我发现个别存在一些问题,很多人喜欢罗列每种加密算法的具体实现,假设我们要求实现AES和Triple DES加密算法,的确可以很多地分别给出它们的具体实现.  那我们真的有必要给出每个加密算法的具体实现吗?而且这样的设计不符合OOP设计思想,最重要的是我们要维护多个加密算法啊!OK接下来让我们实行一个可扩

详解C#中通过委托来实现回调函数功能的方法_C#教程

委托(delegate)是一种可以把引用存储为函数的类型,这类似于c++中的函数指针. 回调函数c++中的回调函数,就是用函数指针来实现的.类似的,c#中用委托,来实现回调函数的功能. 回调函数为什么被称为回调函数?比如你调用了一个函数,那么就叫调用,但是如果你在调用一个函数的时候,还需要把一个函数提供给该函数,让这个函数来调用你的函数,那么你提供的这个函数就被称为回调函数(callback). 对于python这样的动态语言而言,就没有c#,c++提供特殊的语法实现回调函数,因为在python

详解C++中如何将构造函数或析构函数的访问权限定为private_C 语言

今天面试被问到了这个单例模式常用到的技术手段,下面进行分析:         很多情况下要求当前的程序中只有一个object.例如一个程序只有一个和数据库的连接,只有一个鼠标的object.通常我们都将构造函数的声明置于public区段,假如我们将其放入private区段中会发生什么样的后果?这意味着什么?         当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私有的,由于在class外

详解C语言中的ttyname()函数和isatty()函数的用法_C 语言

C语言ttyname()函数:返回一终端机名称头文件: #include <unistd.h> 定义函数: char * ttyname(int desc); 函数说明:如果参数desc 所代表的文件描述词为一终端机, 则会将此终端机名称由一字符串指针返回, 否则返回NULL. 返回值:如果成功则返回指向终端机名称的字符串指针, 有错误情况发生时则返回NULL. 范例 #include <unistd.h> #include <sys/types.h> #include

举例详解CSS中的继承

  CSS的继承是由所使用的样式属性定义的.换句话说,当你查看CSS属性backgruound-color,你会看到一栏「继承性」,也许你几乎没有在意过它,但是它还是十分有用的. 什么是CSS继承 每一个元素都是文档树的一部分,除了最顶级的HTML元素,每个元素都有其对应的父级元素,每一个父级元素的CSS属性值都可以被应用到它的子元素中去. 举个栗子,H1标签包含着一个EM标签: EM就是H1标签的子元素,任何H1中继承的CSS属性值会自动在EM标签中生效,比如: CSS Code复制内容到剪贴

详解Java中格式化日期的DateFormat与SimpleDateFormat类_java

DateFormat 1. DateFormat 介绍DateFormat 的作用是 格式化并解析"日期/时间".实际上,它是Date的格式化工具,它能帮助我们格式化Date,进而将Date转换成我们想要的String字符串供我们使用 不过DateFormat的格式化Date的功能有限,没有SimpleDateFormat强大:但DateFormat是SimpleDateFormat的父类.所以,我们先对DateFormat有个整体了解,然后再学习SimpleDateFormat. D

详解C语言中fseek函数和ftell函数的使用方法_C 语言

fseek函数: int fseek(FILE * _File, long _Offset, int _Origin); 函数设置文件指针stream的位置.如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0.如果执行失败则不改变stream指向的位置,函数返回一个非0值. 超出文件末尾位置,还是返回0.往回偏移超出首位置,还是返回0,小心使用. 第一个参数stream为文件指针. 第二个参数offset为偏移量,正数表示正向偏移,

详解Python中的序列化与反序列化的使用

  这篇文章主要介绍了详解Python中的序列化与反序列化的使用,针对pickle和cPickle对象进行了探究,需要的朋友可以参考下 学习过marshal模块用于序列化和反序列化,但marshal的功能比较薄弱,只支持部分内置数据类型的序列化/反序列化,对于用户自定义的类型就无能为力,同时marshal不支持自引用(递归引用)的对象的序列化.所以直接使用marshal来序列化/反序列化可能不是很方便.还好,python标准库提供了功能更加强大且更加安全的pickle和cPickle模块. cP

详解Android中的Service

Service简介: Service是被设计用来在后台执行一些需要长时间运行的操作. Android由于允许Service在后台运行,甚至在结束Activity后,因此相对来说,Service相比Activity拥有更高的优先级. 创建Service: 要创建一个最基本的Service,需要完成以下工作:1)创建一个Java类,并让其继承Service 2)重写onCreate()和onBind()方法 其中,onCreate()方法是当该Service被创建时执行的方法,onBind()是该S