2----摆脱STL库--之--实现自己的一个String类

       


看完这篇文章,你将学会:

1.C++的的相关实现。

2.操作符重载。

3.字符串的相关知识。

4.#define _CRT_SECURE_NO_WARNINGS

         这是Small Hd来到C++世界的第二天,也就是这天,骑士陪着她开始了寻找大魔王之旅。

第二站--------字符之城(String!String!String!)

        他们首先来到了字符之城。此时正值正午,但是城门却是大开着,不禁让Small Hd怀疑这是不是敌人的空城计。还没等她反应过来,鲁莽的骑士就已经冲进了城中,无奈之下,她也只好跟着进了城。

        嗖!嗖!嗖!伴随着三道声音的响起,二人面前出现了三道巨大的身影,它们分别是

字符熊猫  字符企鹅  字符美美

2----摆脱STL库--之--实现自己的一个String类_第1张图片2----摆脱STL库--之--实现自己的一个String类_第2张图片 2----摆脱STL库--之--实现自己的一个String类_第3张图片

                原来这就是他们此次的挑战对象,不过好在Small Hd已经认真学习过了基本的字符图像打印,利用已有的数学坐标再配合骑士的循环打印输出,三两下便解决了他们。(以后可能会讲到一个打印专题,纯属娱乐!)

不过字符操作怎么能这么简单呢?事出反常必有妖!

        真是说曹操曹操就到,就在Small Hd思考情况不对的时候,天空中飞来一张卷轴,只见上面写道:

        “哈哈哈,好久不见了,Small Hd,你应该猜到我是谁了,不过那又能怎样呢?你的挑战才刚刚开始,哈哈哈哈哈!!!”顷刻,卷轴上面浮现出一行行字迹:

下面先来看看C++的头文件的相关操作,然后完成下面的题目。

C++中的头文件提供了一个字符串类的实现,可以方便地进行字符串的操作。该头文件中定义了一个名为string的类,可以用来表示字符串对象。该类提供了很多有用的成员函数,例如:

1. 构造函数和析构函数:用于创建和销毁字符串对象。

2. 赋值操作符:用于将一个字符串对象的值赋给另一个字符串对象。

3. 比较操作符:用于比较两个字符串对象的值。

4. 子串操作:用于获取一个字符串对象的子串。

5. 查找操作:用于在一个字符串对象中查找指定的子串。

6. 插入和删除操作:用于在一个字符串对象中插入和删除字符或子串。

7. 字符串长度操作:用于获取一个字符串对象的长度。

8. 字符串连接操作:用于将多个字符串对象连接成一个字符串对象。

使用头文件可以方便地进行字符串操作,避免了手动编写字符串处理函数的麻烦。同时,由于该头文件中定义的string类是标准C++库的一部分,因此可以在任何支持C++的平台上使用。

看完上面的解释,你是不是有了一点感觉了呢,让我们开始做题吧!(C++的相关实现见文末!!!)

编程目的

        在不使用STL库,尤其禁止直接使用string的情况下实现一个String类,需要实现以下函数以及重载以下操作符。如果成功实现,将会获得第一块徽章----草系徽章

编程要求

        借助C++ 中的来实现这道题目的行为是被禁止的, 但是/的使用是被允许的

        禁止使用头文件,程序(包括注释中)禁止出现""串!否则将无法获得徽章奖励。

String.h

// String.h
#include
using namespace std;

class String
{
private:
	int len;    // 字符串长度
	char* str_p;    // 字符串数据

public:
	const char* getStr_p()const { return str_p; }//常成员函数,可以直接返回String里的字符串
	String();//默认构造函数

	String(const char* s);//下面是两个构造函数
	String(const String& s);

	~String();     //注意内存泄漏和野指针问题

	void print();  //输出字符串,最后输出换行符\n

	//重载操作符=,[]
	String& operator=(const String& s);
	String& operator=(const char* s);
	char& operator[](int index);

	//TODO:+  字符串拼接 a+b=ab
   // friend String& operator+(String& a,String& b);
	String operator+(const String& s);



	friend bool operator==(const String& s, const String& s1);
	friend bool operator!=(const String& s, const String& s1);
	friend bool operator<(const String& s, const String& s1);
	//TODO:==,!=,<  返回值为bool类型,按ASCII码值比较,约定 a

String.cpp

 //String.cpp
//这是给宏定义,下面会讲到相关用法
#define _CRT_SECURE_NO_WARNINGS

#include "String.h"
#include

String::String()
{
    len = 0;
    str_p = new char[1];
    str_p[0] = '\0';
}

String::String(const char* s)
{

    len = strlen(s);
    str_p = new char[len + 1];
    strcpy(str_p, s);
    str_p[len] = '\0';
}
String::String(const String& s)
{
    len = s.len;
    str_p = new char[len + 1];
    strcpy(str_p, s.getStr_p());

}
String::~String()
{
    if (str_p != nullptr)
    {
        delete[]str_p;
        str_p = nullptr;
        len = 0;
    }

}
void String::print()
{

    cout << str_p << endl;
}

String& String::operator=(const String& s)
{
    if (this != &s) {
        delete[] str_p;
        len = s.len;
        str_p = new char[len + 1];
        strcpy(str_p, s.getStr_p());
    }
    return *this;
}
String& String::operator=(const char* s)
{
    delete[] str_p;
    len = strlen(s);
    str_p = new char[len + 1];
    strcpy(str_p, s);
    return *this;
}
char& String::operator[](int index)
{
    return str_p[index];

}

/*//这样写似乎也可以
String& String::operator+(String& a, String& b)
{
    static String string;
    int len_total = strlen(a.getStr_p()) + strlen(b.getStr_p()) + 1;
    string.len = len_total;
    strcpy(string.getStr_p(), a.getStr_p());
    strcat(string.getStr_p(), b.getStr_p());
    return string;
}
*/

String String::operator+(const String& s) {
    int newlen = len + s.len;
    char* newstrp = new char[newlen + 1];
    strcpy(newstrp, str_p);
    strcat(newstrp, s.getStr_p());
    String newstr(newstrp);
    delete[] newstrp;
    return newstr;
}

bool operator==(const String& s, const String& s1) {
    return (strcmp(s.getStr_p(), s1.getStr_p()) == 0);
}

bool operator!=(const String& s, const String& s1) {
    return (strcmp(s.getStr_p(), s1.getStr_p()) != 0);
}

bool operator<(const String& s, const String& s1) {
    return (strcmp(s.getStr_p(), s1.getStr_p()) < 0);
}
int main(void)
{
    String s0; //构造函数
    s0.print();
  //  输出:
    s0 = "abc";
    s0.print();
  //  输出:abc
   String s1(s0);  //构造函数
    s1.print();
   // 输出:abc
   s1.~String(); //析构函数,s1的析构不会影响s0
  s0.print();
    //输出:abc
   String s2("dd");  //构造函数
   s2.print();
   //输出:dd
    String s3 = s2;  //重载 =
    s2.~String();  //析构函数,s2的析构不会影响s3
    s3.print();
    //输出:d
   // String s4 = s3 + s3 + s3;  //重载 +,加法会考察连续+,且加法不会影响加数
    String s4("aaaaa");
    s4.print();
    输出:ddd
    cout << s4[2] << endl;  //重载 []
    输出:d
    cout << (s4 == s3) << endl;  //重载 ==
    输出0
    cout << (s4 != s3) << endl;  //重载 !=
    输出1
    cout << (s4 < s3) << endl;  //重载 <
    //输出0
    return 0;
}






下面是骑士的输出:

2----摆脱STL库--之--实现自己的一个String类_第4张图片

        我们可以看到,输出是正确的,因此Small Hd正确的完成了这道题目,她将拿回自己的第一块徽章。不过,在此之前,她要先做一个总结:

“C++有关类的一些访问控制我不多说,大家在网上都可以查到。我主要提一些注意点:

1.#define _CRT_SECURE_NO_WARNINGS

        产生原因:因为某些.c /.cpp文件使用了strcpy,scanf等不安全的函数,而报警告和错误,而导致无法编译通过(或许是VS才有?)。例如:严重性    代码    说明    项目    文件    行    禁止显示状态错误    C4996    'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.    Project5    D:\VisualCode\Project5\String.cpp    20    

此时我们有两种解决方案:

        a.在指定的源文件的开头定义:#define _CRT_SECURE_NO_WARNINGS (只会在该文件里起作用)

        b.在项目属性里设置,这会在整个项目里生效,依次选择:属性->配置属性->C/C++ ->预处理器->预处理器定义->编辑    最下面加上一行:_CRT_SECURE_NO_WARNINGS (注意不需要#define)

2.字符串的长度:

        字符串的长度是指字符串所含的字符个数,并不包括最后的'\0'

        字符串的大小即内存空间的大小, 由字符串长度加1得到。(定义字符串长度有两种方式, 一种是用字符串的大小, 一种是字符串的长度.)

        C语言的字符串是由字符数组形式保存的,并约定'\0'(ascii码值为0)作为字符串结束符。其长度为从字符串开始,到'\0'结束,所有字符的个数,不包括'\0'本身。

        关键在于C字符串有个结尾0, 处理时需要注意.”

3.几种常见的操作符重载 ----精讲

当我们在C++中使用自定义的类时,可以通过重载操作符来实现对该类的各种操作。下面是一些常见的操作符重载及其相关的代码实现。

1. 赋值操作符(=)

赋值操作符用于将一个对象的值赋给另一个对象。在自定义的类中,我们需要重载赋值操作符,以便正确地复制对象的值。例如:

```
class MyClass {
public:
    int value;

    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            value = other.value;
        }
        return *this;
    }
};
```

在上述代码中,我们重载了赋值操作符,使它能够正确地复制对象的值。注意,赋值操作符的返回值应该是一个引用,以便支持连续赋值。

2. 相等操作符(==)

相等操作符用于比较两个对象是否相等。在自定义的类中,我们需要重载相等操作符,以便正确地比较对象的值。例如:

```
class MyClass {
public:
    int value;

    bool operator==(const MyClass& other) const {
        return value == other.value;
    }
};
```

在上述代码中,我们重载了相等操作符,使它能够正确地比较对象的值。注意,相等操作符的返回值应该是一个布尔值。

3. 不等操作符(!=)

不等操作符用于比较两个对象是否不相等。在自定义的类中,我们需要重载不等操作符,以便正确地比较对象的值。例如:

```
class MyClass {
public:
    int value;

    bool operator!=(const MyClass& other) const {
        return value != other.value;
    }
};
```

在上述代码中,我们重载了不等操作符,使它能够正确地比较对象的值。注意,不等操作符的返回值应该是一个布尔值。

4. 加法操作符(+)

加法操作符用于将两个对象相加。在自定义的类中,我们需要重载加法操作符,以便正确地执行加法操作。例如:

```
class MyClass {
public:
    int value;

    MyClass operator+(const MyClass& other) const {
        MyClass result;
        result.value = value + other.value;
        return result;
    }
};
```

在上述代码中,我们重载了加法操作符,使它能够正确地执行加法操作。注意,加法操作符的返回值应该是一个新的对象,而不是修改原有的对象。

5. 减法操作符(-)

减法操作符用于将两个对象相减。在自定义的类中,我们需要重载减法操作符,以便正确地执行减法操作。例如:

```
class MyClass {
public:
    int value;

    MyClass operator-(const MyClass& other) const {
        MyClass result;
        result.value = value - other.value;
        return result;
    }
};
```

在上述代码中,我们重载了减法操作符,使它能够正确地执行减法操作。注意,减法操作符的返回值应该是一个新的对象,而不是修改原有的对象。

6. 自增操作符(++)

自增操作符用于将对象的值增加1。在自定义的类中,我们需要重载自增操作符,以便正确地执行自增操作。例如:

```
class MyClass {
public:
    int value;

    MyClass& operator++() {
        value++;
        return *this;
    }
};
```

在上述代码中,我们重载了自增操作符,使它能够正确地执行自增操作。注意,自增操作符的返回值应该是一个引用,以便支持连续自增。

7. 自减操作符(--)

自减操作符用于将对象的值减少1。在自定义的类中,我们需要重载自减操作符,以便正确地执行自减操作。例如:

```
class MyClass {
public:
    int value;

    MyClass& operator--() {
        value--;
        return *this;
    }
};
```

在上述代码中,我们重载了自减操作符,使它能够正确地执行自减操作。注意,自减操作符的返回值应该是一个引用,以便支持连续自减。

8. new 和 delete 操作符

new 和 delete 操作符用于动态分配和释放内存。在自定义的类中,我们需要重载这些操作符,以便正确地分配和释放对象的内存。例如:

```
class MyClass {
public:
    void* operator new(size_t size) {
        void* p = malloc(size);
        return p;
    }

    void operator delete(void* p) {
        free(p);
    }
};
```

在上述代码中,我们重载了 new 和 delete 操作符,使它们能够正确地分配和释放对象的内存。注意,new 操作符的返回值应该是一个指向分配的内存的指针,而 delete 操作符不需要返回值。

9. 下标操作符([])

下标操作符用于访问对象中的元素。在自定义的类中,我们需要重载下标操作符,以便正确地访问对象中的元素。例如:

```
class MyClass {
public:
    int values[10];

    int& operator[](int index) {
        return values[index];
    }
};
```

在上述代码中,我们重载了下标操作符,使它能够正确地访问对象中的元素。注意,下标操作符的返回值应该是一个引用,以便支持对元素的修改。

10. 大于操作符(>)

大于操作符用于比较两个对象的大小关系。在自定义的类中,我们需要重载大于操作符,以便正确地比较对象的大小。例如:

```
class MyClass {
public:
    int value;

    bool operator>(const MyClass& other) const {
        return value > other.value;
    }
};
```

在上述代码中,我们重载了大于操作符,使它能够正确地比较对象的大小。注意,大于操作符的返回值应该是一个布尔值。

11. 小于操作符(<)

小于操作符用于比较两个对象的大小关系。在自定义的类中,我们需要重载小于操作符,以便正确地比较对象的大小。例如:

```
class MyClass {
public:
    int value;

    bool operator<(const MyClass& other) const {
        return value < other.value;
    }
};
```

在上述代码中,我们重载了小于操作符,使它能够正确地比较对象的大小。注意,小于操作符的返回值应该是一个布尔值。

以上是一些其他常见的操作符重载及其相关的代码实现。在实际编程中,我们可以根据需要重载其他操作符,以便正确地操作自定义的类。

以上是一些常见的操作符重载及其相关的代码实现。在实际编程中,我们可以根据需要重载其他操作符,以便正确地操作自定义的类。

4.C++的相关实现:

以下是库的所有实现代码:

1.构造函数

//默认构造函数 string s1;

//构造函数 string s2("hello");

//拷贝构造函数 string s3(s2);

2.赋值操作符

string s1 = "hello";

string s2;

s2 = s1;

3.字符串连接

string s1 = "hello";

string s2 = "world";

string s3 = s1 + " " + s2;

4.字符串比较

string s1 = "hello";

string s2 = "world";

if(s1 == s2) { cout << "s1和s2相等" << endl; }

else if(s1 < s2) { cout << "s1小于s2" << endl; }

else { cout << "s1大于s2" << endl; }

5.字符串查找

string s1 = "hello world";

int pos = s1.find("world");

6.字符串替换

string s1 = "hello world";

s1.replace(6, 5, "there");

7.字符串插入

string s1 = "hello";

s1.insert(2, "world");

8.字符串删除

string s1 = "hello world";

s1.erase(6, 5);

9.字符串长度

string s1 = "hello";

int len = s1.length();

完整代码如下:


#include 
#include 

using namespace std;

int main()
{
    //默认构造函数
    string s1;

    //构造函数
    string s2("hello");

    //拷贝构造函数
    string s3(s2);

    //赋值操作符
    string s4 = "hello";
    string s5;
    s5 = s4;

    //字符串连接
    string s6 = "hello";
    string s7 = "world";
    string s8 = s6 + " " + s7;
    cout << s8 << endl;

    //字符串比较
    string s9 = "hello";
    string s10 = "world";
    if(s9 == s10)
    {
        cout << "s9和s10相等" << endl;
    }
    else if(s9 < s10)
    {
        cout << "s9小于s10" << endl;
    }
    else
    {
        cout << "s9大于s10" << endl;
    }

    //字符串查找
    string s11 = "hello world";
    int pos = s11.find("world");
    if(pos != -1)
    {
        cout << "world在s11中的位置为:" << pos << endl;
    }

    //字符串替换
    string s12 = "hello world";
    s12.replace(6, 5, "there");
    cout << s12 << endl;

    //字符串插入
    string s13 = "hello";
    s13.insert(2, "world");
    cout << s13 << endl;

    //字符串删除
    string s14 = "hello world";
    s14.erase(6, 5);
    cout << s14 << endl;

    //字符串长度
    string s15 = "hello";
    int len = s15.length();
    cout << "s15的长度为:" << len << endl;

    return 0;
}

以上就是C++ 的库的相关实现,相信你已经学会了呢!

//当Small Hd总结完成后,只见天空突然变成了绿色,接着,一股熟悉的气息奔涌而来

2----摆脱STL库--之--实现自己的一个String类_第5张图片

草系徽章------到手!

         下一次Small Hd 又会遇到怎样的风险与挑战呢?让我们拭目以待吧。


 

你可能感兴趣的:(C++,c++)