看完这篇文章,你将学会:
1.C++的
的相关实现。 2.操作符重载。
3.字符串的相关知识。
4.#define _CRT_SECURE_NO_WARNINGS
这是Small Hd来到C++世界的第二天,也就是这天,骑士陪着她开始了寻找大魔王之旅。
第二站--------字符之城(String!String!String!)
他们首先来到了字符之城。此时正值正午,但是城门却是大开着,不禁让Small Hd怀疑这是不是敌人的空城计。还没等她反应过来,鲁莽的骑士就已经冲进了城中,无奈之下,她也只好跟着进了城。
嗖!嗖!嗖!伴随着三道声音的响起,二人面前出现了三道巨大的身影,它们分别是
字符熊猫 字符企鹅 字符美美
原来这就是他们此次的挑战对象,不过好在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;
}
下面是骑士的输出:
我们可以看到,输出是正确的,因此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总结完成后,只见天空突然变成了绿色,接着,一股熟悉的气息奔涌而来
草系徽章------到手!
下一次Small Hd 又会遇到怎样的风险与挑战呢?让我们拭目以待吧。