实现buffer操作
鉴于此设计,大多数编辑器操作非常容易实现。可以通过向游标标字段的内容分配新值来实现移动游标的四个操作中的每一个。例如,移动到buffer的开始,只需要将值0分配给游标; 将其移动到最后只是将length的值复制到cursor中的问题。我们可以在EditorBuffer类的完整实现中看到这些简单方法的代码,如下所示:
/*
*这个文件利用数组来实现buffer.h中的内容
*/
#include <iostream>
#include <cctype>
#include <string>
#include <cstdlib>
#include "buffer.h"
using namespace std;
/*常数*/
const int INITIAL_CAPACITY = 10; //初始化的容量为10
/*
*函数说明:构造函数和析构函数
*----------------------------
*构造函数初始化私有成员的值,析构函数释放堆中的对象所占的空间
*/
EditorBuffer::EditorBuffer(){
length = 0;
cursor = 0;
capacity = INITIAL_CAPACITY;
array = new char[capacity]; //动态数组分配
}
EditorBuffer::~EditorBuffer(){
delete[] array;
}
/*
*实现移动右游标的方法
*/
void EditorBuffer::moveCursorForward(){
if(cursor < length) cursor ++;
}
void EditorBuffer::moveCursorBackward(){
if(cursor > 0) cursor--;
}
void EditorBuffer::moveCursorToStart(){
cursor = 0;
}
void EditorBuffer::moveCursorToEnd(){
cursor = length;
}
/*
*实现的方法:delecteCharacter(); insertCharacter(char ch);
*-----------------------------------
*插入或删除字符的每个功能都必须移动数组中的所有后续字符,
*以便为新插入留出空间,或者关闭删除留下的空间
*/
void EditorBuffer::insertCharacter(char ch){
if(cursor == length) expandCapacity();
/*
*这里我们选择从光标到buffer末尾的这一段字符
*然后我们将前一位的值赋给后一位,实现腾出空
*间的操作,使得ch可以插入
*/
for(int i = length; i > cursor; i--){
array[i] = array[i - 1];
}
array [cursor] = ch; //将ch的值赋值给当前的光标处
cursor++;
length++;//插入后,字符数组的长度相应加1
}
void EditorBuffer::deleteCharacter(){
if(cursor < length){
/*
*这里我们选择从光标 +1 到buffer末尾的这一段字符
*然后我们将后一位的值赋给前一位,实现收缩空
*间的操作
*/
for(int i = cursor + 1; i < length; i++){
array[i - 1] = array[i];
}
length--;//插入后,字符数组的长度相应减1
}
}
/*
*方法 :showContent
*------------------
*该方法打印缓冲区的内容,每个字符之间有
*一个空格,留下下一行插入符号的空间,以指示光标的位置
*/
void EditorBuffer::showContents(){
for(int i = 0; i < length; i++){
cout << " " << array[i];
}
cout << endl;
cout << string(2 * cursor, ' ') << "^" << endl;
}
/*
*方法:expandCapacity()
*---------------------
*这个方法的原理应该记住,我们将原array在堆中的首节点地址赋给我们定义的
*相应类型的 oldArray,此时他们两个指向堆中的同一个数组
*然后将capacity扩大一倍,在堆中建立一个新容量为capacity *2的数组
*之后把原oldArray指向的内容复制给现在扩容后的array
*最后删除oldArray中的内容,完成扩容操作
*/
void EditorBuffer::expandCapacity(){
char * oldArray = array;
capacity *= 2;
array = new char[capacity];
for(int i = 0; i < length; i++){
array[i] = oldArray[i]; //复制
}
delete[] oldArray;
}
代码分析:
上述代码中唯一需要讨论的操作是构造函数,析构函数和insertCharacter和deleteCharacter方法。 因为这些方法可能看起来有点棘手,特别是对于第一次实现这些实现的人来说,值得在代码中写下其操作的注释。
构造函数负责初始化表示空缓冲区的实例变量,因此构造函数的注释是描述这些实例变量及其代表的一个好的地方。析构函数被释放任何动态分配的存储,在存储期间被对象获取。 对于基于数组的EditorBuffer实现,唯一动态分配的内存是用于保存文本的数组。 因此,析构函数的代码是这样的:
delete[] array;
insertCharacter和deleteCharacter方法很有趣,因为它们中的每一个都需要在数组中移动字符,以便为要插入的字符腾出空间,或者关闭被删除的字符留下的空格。例如,要插入缓冲区中光标位置处的字符X:
要在buffer的数组表示中这样做,首先需要确保数组中有空间。如果长度等于容量,则当前分配的数组中没有更多的空间来容纳新字符。在这种情况下,有必要扩展阵列容量,这里我们用:
expandCapacity();
这个方法来实现,这个操作很有意思,多多留意。
然而,数组中的额外空间完全在最后的操作。要在中间插入一个字符,你需要在光标的当前位置(3)为该字符腾出空间。得到该空间的唯一方法是将剩余的字符的位置向右移动,使缓buffer结构处于以下状态:
数组中产生的间距为你提供插入X所需的空间,之后光标前进,使其跟随新插入的字符之后,留下以下配置:
deleteCharacter操作是类似的,因为它需要一个循环来关闭被删除的字符留下的空白。