1. 分析C语言中字符串的缺陷
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象)的思想,而且底层空间需要用户自己管理,稍不留神可
能还会越界访问。
- 熟悉string类的常见接口并熟练应用,完成在线OJ题目
翻转字符串I
class Solution {
public:
string reverseString(string s) {
if(s.empty())
return s;
size_t start = 0;
size_t end = s.size()-1;
while(start < end)
{
swap(s[start], s[end]);
++start;
--end;
}
return s;
}
};
class Solution {
public:
int firstUniqChar(string s) {
// 统计每个字符出现的次数
int count[256] = {0};
int size = s.size();
for(int i = 0; i < size; ++i)
count[s[i]] += 1;
// 按照字符次序从前往后找只出现一次的字符
for(int i = 0; i < size; ++i)
if(1 == count[s[i]])
return i;
return -1;
}
};
3. 熟悉什么是浅拷贝?浅拷贝会存在什么危害 ?
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为有效,所以当继续对资源进行操作时,就会发生访问违规。
4. 浅拷贝解决方法一:深拷贝
深拷贝:给每个对象独立分配资源,保证对多个对象之间不会因共享资源而造成多次释放,造成程序崩溃问题。
深拷贝代码实现
class string {
string(const char*str = "")
{
if(str == nullptr)
{
str = "";
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
string(const string& s):_str = nullptr
{
string strTmp(s._str);
swap(_str,strTmp);
}
}
5. 浅拷贝解决方式二:写时拷贝(熟悉写时拷贝原理,为什么要引入引用计数,引用计数为什么要设置成指针方式,),并分析写时拷贝的缺点
写是拷贝就是编程界“懒惰行为”——拖延战术的产物。举个例子昨天晚上你吃完晚饭但是不想洗碗,到第二天中午再次吃饭时才去洗昨天晚上的碗。这就是一种非常典型的“懒惰行为”。
要实现写时才拷贝,需要解决两个问题,一个是内存共享(俩个指针同时可以读取到同一内存上的数据),一个是Copy-On-Wirte(当要对数据进行写时重新开辟内存,在新开辟的内存上对数据进行修改)。
1、 Copy-On-Write的原理是什么?
Copy-On-Write一定使用了“引用计数”,是的,必然有一个变量类似于RefCnt。当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加,当有类析构时,这个计数会减一,直到最后一个类析构时,此时的RefCnt为1或是0,此时,程序才会真正的Free这块从堆上分配的内存。
引用计数就是string类中写时才拷贝的原理!
2、 string类在什么情况下才共享内存的?
1)以别的类构造自己,2)以别的类赋值。第一种情况时会触发拷贝构造函数,第二种情况会触发赋值操作符。这两种情况我们都可以在类中实现其对应的方法。对于第一种情况,只需要在string类的拷贝构造函数中做点处理,让其引用计数累加;同样,对于第二种情况,只需要重载string类的赋值操作符,同样在其中加上一点处理。
3、 string类在什么情况下触发写时才拷贝(Copy-On-Write)?
当然是在共享同一块内存的类发生内容改变时,才会发生Copy-On-Write。比如string类的[]、=、+=、+、操作符赋值,还有一些string类中诸如insert、replace、append等成员函数,包括类的析构时。
修改数据会触发Copy-On-Write。
4、 Copy-On-Write时,发生了什么?
If ( RefCnt>0 ) {
char* tmp = (char*) malloc(strlen(_Ptr)+1);
strcpy(tmp, _Ptr);
_Ptr = tmp;
}
通过代码我们可以知道 ,Copy-On-Write时通过对“计数”的判断,然后开辟空间,最后将_Ptr的内容拷贝给tmp并将内存交换。
5、Copy-On-Write的缺点内存泄漏
int main(void) {
string the_base(1024 * 1024 * 10, 'x');
fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] );
long begin = getcurrenttick();
for (int i = 0; i < 100; i++) {
string the_copy = the_base;
}
fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );
}
C++标准认为,当你通过迭代器或[]获取到string的内部地址的时候,string并不知道你将是要读还是要写。这是它无法确定,如果你要写而它认为读,那没就会是浅拷贝,很可能导致内存泄漏。为此,当你获取到内部引用后,为了避免不能捕获你的写操作,它在此时废止了写时才拷贝技术!