字符串对象的底层可以是char *_pstr
;然后实现四大函数-析构,构造,赋值构造,拷贝构造
class String
{
public:
String(const char *p = nullptr)
{
if (p != nullptr)
{
_pstr = new char[strlen(p) + 1]; // strlen是有效字符的个数
strcpy(_pstr, p);
}
else
{
_pstr = new char[1]; // 为空也开辟一个空间赋值为'\0'
*_pstr = '\0';
}
}
~String()
{
delete[]_pstr;
_pstr = nullptr;
}
String(const String &str) // 拷贝构造函数
{
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
}
String& operator=(const String &str) // 赋值构造函数
{
if (this == &str) // 防止自赋值
return *this;
delete[]_pstr; // 释放原先的内存
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
return *this;
}
private:
char *_pstr;
};
接下来在public属性中写String的大于小于等于和括号运算符的重载
class String
{
public:
// 省略
bool operator>(const String &str)const
{
return strcmp(_pstr, str._pstr) > 0;
}
bool operator<(const String &str)const
{
return strcmp(_pstr, str._pstr) < 0;
}
bool operator==(const String &str)const
{
return strcmp(_pstr, str._pstr) == 0;
}
int length()const { return strlen(_pstr); } // 返回字符串长度
const char* c_str()const { return _pstr; } // 转为C语言格式的字符串
// char ch = str6[6]; str6[6] = '7'
char& operator[](int index) { return _pstr[index]; } // 重载[]运算符,普通方法可修改
// char ch = str6[6]; 不允许修改!str6[6] = '7'
const char& operator[](int index)const { return _pstr[index]; } // 常方法不允许修改
// 省略
};
然后在public属性中实现迭代器方法。
首先要了解迭代器的使用:
#include <string>
using namespace std;
int main()
{
string str1 = "hello world!";
for(std::string::iterator it = str1.begin(); it != str1.end(); ++it)
{
cout << *it << " ";
}
}
str1.begin()
访问到的是数据中的'h'zi字母,str1.end()
实际上是最后一位的后继,所以这一位是访问不到的也被作为判断字符串的结束位置。
迭代器可以透明的访问容器内部的元素的值。
下面来实现迭代器,每种容器的迭代器往往有不同的操作,所以迭代器往往内嵌到类中。
class String
{
public:
// 省略
// 给String字符串类型提供迭代器的实现
class iterator
{
public:
iterator(char *p = nullptr) :_p(p) {}
bool operator!=(const iterator &it) // 实现!=的重载
{
return _p != it._p; // 当前迭代器指针 _p 与 迭代器指针it._p进行比较
}
void operator++() // 实现++的重载
{
++_p;
}
char& operator*() { return *_p; } // 迭代器解引用
private:
char *_p; // 用指针来访问String底层的每一个char型元素
};
// begin返回的是容器底层首元素的迭代器的表示
iterator begin() { return iterator(_pstr); }
// end返回的是容器末尾元素后继位置的迭代器的表示
iterator end() { return iterator(_pstr + length()); }
// 省略
}
完整源文件 点此
迭代器失效的原因:
在STL里,不能以指针来看待迭代器,指针是与内存绑定的,而迭代器是与容器里的元素绑定的,删除了之后,该迭代器就失效了,在对其重新赋值之前,不能再访问此迭代器。insert也类似,迭代器与元素绑定,当前元素被insert的元素占了自然失效了。
迭代器的失效问题?
- 迭代器为什么会失效? a:当容器调用erase方法后,当前位置到容器末尾元素的所有的迭代器全部失效了 b:当容器调用insert方法后,当前位置到容器末尾元素的所有的迭代器全部失效了
迭代器依然有效 迭代器全部失效
首元素 -> 插入点/删除点 -> 末尾元素
c:insert来说,如果引起容器内存扩容
原来容器的所有的迭代器就全部失效了
首元素 -> 插入点/删除点 -> 末尾元素
d:不同容器的迭代器是不能进行比较运算的
- 迭代器失效了以后,问题该怎么解决? 对插入/删除点的迭代器进行更新操作
#include <ctime>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 1.场景1
vector<int> vec;
for(int i = 0; i < 20; ++i)
{
vec.push_back(rand() % 100 + 1);
}
// 把vec容器中所有的偶数全部删除
auto it = vec.begin();
for(; it != vec.end(); ++it)
{
if(*it % 2 == 0)
{
// 迭代器失效的问题,第一次调用erase以后,迭代器it就失效了
vec.erase(it); // insert(it, val) erase(it)
//break; // 用break只删除一个运行正确,去掉break进程出现异常
}
}
// 2. 场景2
vector<int> vec1;
for(int i = 0; i < 20; ++i)
{
vec1.push_back(rand() % 100 + 1);
}
// 给vec容器中所有的偶数前面添加一个小于偶数值1的数字
auto it1 = vec1.begin();
for (; it1 != vec1.end(); ++it1)
{
if (*it1 % 2 == 0)
{
// 这里的迭代器在第一次insert之后,iterator就失效了
vec1.insert(it1, *it1-1);
//++it1;
//break;
}
}
return 0;
}
怎么解决呢?看如下代码
#include <ctime>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 1.场景1
vector<int> vec;
for(int i = 0; i < 20; ++i)
{
vec.push_back(rand() % 100 + 1);
}
for ( int v : vec)
{
cout << v << " ";
}
cout << endl;
// 把vec容器中所有的偶数全部删除
auto it = vec.begin();
for(; it != vec.end(); ) // 返回了新的iterator,当没有删除操作时才需要++it
{
if(*it % 2 == 0)
{
it = vec.erase(it); // insert(it, val) erase(it)
//break; // 用break只删除一个运行正确,去掉break进程出现异常
}
else
{
++it;
}
}
for ( int v : vec)
{
cout << v << " ";
}
cout << endl;
// 2. 场景2
vector<int> vec1;
for(int i = 0; i < 20; ++i)
{
vec1.push_back(rand() % 100 + 1);
}
// 给vec容器中所有的偶数前面添加一个小于偶数值1的数字
auto it1 = vec1.begin();
for (; it1 != vec1.end(); ++it1)
{
if (*it1 % 2 == 0)
{
it1 = vec1.insert(it1, *it1-1);
++it1; // 碰到偶数在当前位置插入后,要往后走一步
//break;
}
}
return 0;
}
比对失效和不失效的情况:
vec.erase(it);
vec1.insert(it1, *it1-1);
// 上面是失效的迭代器,只能执行一次。
// 只要不断更新迭代器迭代器就不会失效
it = vec.erase(it);
it1 = vec1.insert(it1, *it1-1);
- malloc和new的区别?
a.malloc按字节开辟内存的;new开辟内存时需要指定类型 new int[10],new分配内存是按照类型分配,malloc分配内存按照大小分配
所以malloc开辟内存返回的都是
void*
operator new -> int*
。所以malloc分配后往往要强制转换。 b.malloc只负责开辟空间,new不仅仅有malloc的功能,可以进行数据的初始化new int(20);
<-赋初值new int[20];
new int[20]();
<-数组每个元素赋为0int()
c.malloc开辟内存失败返回nullptr指针;new抛出的是bad_alloc类型的异常 d.new是一个操作符可以重载,而malloc是一个库函数。 e.malloc分配内存时可以通过realloc扩容的。new不行。malloc扩容原理是看下面代码
ptr = malloc(sizeof(int));
ptr1 = realloc(ptr, count * sizeof(int));
if (ptr1 == NULL) // reallocated pointer ptr1
{
printf("\nExiting!!");
free(ptr);
exit(0);
}
else
{
ptr = ptr1; // the reallocation succeeded, we can overwrite our original pointer now
}
realloc实际上是free(ptr)然后重新分配空间。
- free和delete的区别?
delete: 先调用析构函数, 再free
如果delete后面就是普通指针,如下:
delete (int*)p
和free(p)
没有区别new -> operator new
delete -> operator delete
new和delete能混用吗?=>new[]和delete能混用吗 C++为什么区分单个元素和数组的内存分配和释放呢? new delete new[] delete[] 对于普通的编译器内置类型 new/delete[] new[]/delete => new和delete[] 以及 new[]和delete可以混用 自定义的类类型,有析构函数,为了调用正确的析构函数,那么开辟对象数组的时候,不可以混用。
开辟对象数组new会多开辟4个字节(相比于malloc),记录对象的个数。
下面来通过malloc和free实现new和delete,new[]
和delete[]
#include <iostream>
using namespace std;
// 如果是对象应该:先调用operator new开辟内存空间、然后调用对象的构造函数(初始化)
void* operator new(size_t size)
{
void *p = malloc(size);
if (p == nullptr)
throw bad_alloc();
cout << "operator new addr:" << p << endl;
return p;
}
// delete p; 如果是对象应该:调用p指向对象的析构函数、再调用operator delete释放内存空间
void operator delete(void *ptr)
{
cout << "operator delete addr:" << ptr << endl;
free(ptr);
}
// 处理数组的new和delete
void* operator new[](size_t size)
{
void *p = malloc(size);
if (p == nullptr)
throw bad_alloc();
cout << "operator new[] addr:" << p << endl;
return p;
}
void operator delete[](void *ptr)
{
cout << "operator delete[] addr:" << ptr << endl;
free(ptr);
}
class Test
{
public:
Test(int data = 10) { cout << "Test()" << endl; }
~Test() { cout << "~Test()" << endl; }
private:
int ma;
};
int main()
{
/*
通过汇编可以看到,new和delete在汇编指令上表现为:
call operator new 和 call operator delete
可以得知new和delete本质上是重载函数的调用
*/
try
{
int *p = new int;
delete p;
int *q = new int[10];
delete []q;
}
catch (const bad_alloc &err)
{
cerr << err.what() << endl;
}
Test *p1 = new Test();
delete []p1;
/*
operator new[] addr:00940268
Test()
Test()
Test()
Test()
Test()
~Test()
operator delete addr:0094026C
*/
Test *p2 = new Test[5];
cout << "p2:" << p2 << endl;
delete[] p2; // Test[0]对象析构, 直接free(p2)
return 0;
}