知识点列举
- 使用初始化列表的好处
- 拷贝构造的形参为&与值的区别
- 深拷贝与浅拷贝
- 使用&的好处
- 形参使用const引用的好处
使用初始化列表的好处
- 类成员中存在常量,如const int a,只能用初始化不能复制
- 类成员中存在引用,同样只能使用初始化不能赋值。类成员中存在引用,同样只能使用初始化不能赋值。
- 提高效率:对类类型来说,用初始化列表少了一次调用默认构造函数的过程,
//模拟实现string
class String
{
public:
//构造函数
String(const char* str =“”)
:_size(strlen(str))
, _str(new char[_size + 1])
,_capacity( _size)
{
strcpy(_str, str);
}
String(char* str, size_t size)
:_size(size)
,_capacity(size)
,_str(_str = new char[size + 1])
{
strncpy(_str, str, size);
}
拷贝构造的形参为&与值的区别
如果拷贝构造函数的形参用值,由于类中没有这个对象,就要调用拷贝构造函数来创建,以此类推,会造成无限递归循环
//拷贝构造函数
String(const String& s)
:_str(new char[s._capacity + 1])
, _capacity(s._capacity)
, _size(s._size)
{
strcpy(_str, s._str);
}
深拷贝与浅拷贝
- 浅拷贝:也称位拷贝,编译器只是直接将指针的值全部拷贝过来,从而造成对个对象使用同一块空间,当一个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,依旧会对这块空间进行释放,在对这段内存进行释放操作的时候,一个内存空间被释放多次,发生了访问违规,导致程序崩溃。
- 深拷贝:为了解决浅拷贝的问题,深拷贝则不是直接将指针的值拷贝,是为指针_str开辟与str相同大小的内存空间,然后将str中的资源拷贝到_str中,这样虽然_str与str中的资源一样,但是使用的是两块独立的空间,所以delete时只释放这个对象自己的内存,不会出现访问违规。
//赋值运算符
String operator=(const String& s)
{
if (this != &s)
{
char* pStr = new char[s._capacity + 1];
strcpy(pStr, _str);
delete[] _str;
_str = pStr;
_capacity = s._capacity;
_size = s._size;
}
return *this;
}
形参使用&的好处
形参是对象的引用,是通过传地址的方法传递参数的,对函数形参的改变就是对实参的改变,如果函数的形参是对象,则是通过传值的方法传递参数的,函数体内对形参的修改无法传到函数体外。
运算符重载部分
//运算符重载部分
bool operator<(const String& s)
{
return !strcmp(_str, s._str);
}
bool operator<=(const String& s)
{
if (strcmp(_str, s._str) == 1)
return npos;
return 1;
}
bool operator>(const String& s)
{
return strcmp(_str, s._str);
}
bool operator>=(const String& s)
{
if (strcmp(_str, s._str) != 1)
return npos;
return 1;
}
bool operator==(const String& s)
{
return (strcmp(_str, s._str) == 0);
}
bool operator!=(const String& s)
{
return (strcmp(_str, s._str) != 0);
}
friend ostream& operator<<(ostream& _cout, const String& s)
{
_cout << s._str;
return _cout;
}
friend istream& operator>>(istream& _cin, String& s)
{
_cin >> s._str;
return _cin;
}
形参使用const的好处
- 当实参的类型比较大时,复制开销很大,引用会“避免复制”。
- “避免修改实参”,当使用引用时,如果调用者希望只使用实参并不修改实参,则const可以避免使用该引用修改实参。
string中部分接口的模拟实现
//尾插
void PushBack(char c)
{
//CheckCapacity();
_str[_size++] = c;
_str[_size] = '\0';
}
//追加字符串
void Append(const char* str)
{
//判断对象中的剩余空间是否能够放下,放不下就开辟新空间
int len = strlen(str);
if (len > _capacity - _size)
Reserve(2 * _capacity + len);
strcat(_str, str);
}
char& operator[](size_t index)
{
return _str[index];
}
const char& operator[](size_t index)const
//at与[]唯一不同的方式是前者越界会抛出异常,后者越界会崩溃,或者给随机值
{
return _str[index];
}
void Reserve(size_t newCapacity)
{
if (_capacity <= newCapacity && _capacity != newCapacity)
{
char* pStr = new char[newCapacity + 1];
strcpy(pStr, _str);
delete[] _str;
_str = pStr;
_capacity = newCapacity;
}
}
void ReSize(size_t newSize, char c)
{
if (newSize > _size)
{
/*自己实现memset函数
size_t size = _size;
for (size_t i = 0; i < newSize; ++i)
{
pStr[size++] = c;
}
delete[] _str;
_str = pStr;*/
Reserve(newSize);
memset(_str + _size, c, newSize - _size);
_str[newSize] = '\0';
_capacity = newSize;
}
else if (newSize < _size)
{
/*char* pStr = new char[newSize + 1];
for (size_t i = 0; i < newSize; ++i)
{
pStr[i] = _str[i];
}
delete[] _str;
_str = pStr;*/
memset(_str + newSize, '\0', 1);
}
_size = newSize;
}
int Size()const
{
return _size;
}
bool Empty()const
{
return (0 == _size);
}
int Find(char c, size_t pos = 0)
{
for (size_t i = pos; i < _size; ++i)
{
if (c == _str[i])
return i;
}
return npos;
}
int rFind(char c)
{
for (size_t i = _size - 1; i > 0; --i)
{
if (c == _str[i])
return i;
}
return npos;
}
//返回c格式的字符串
const char* C_str()const //返回c格式的字符串
{
return _str;
}
void Swap(String& s)
{
swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._capacity);
}
//从_str中返回从pos开始的size个字符的子字符串
String StrSub(size_t pos, size_t size)
{
//统计pos之后的所有字符数量
int len = strlen(_str + pos);
//如果size超过了字符串实际长度,将size设置为pos之后的所有字符数量
if (len < size)
size = len;
//将_str中从pos位置开始的字符串传递size个回去
return String(_str + pos, size);
}
//析构函数
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
char* _str;
size_t _capacity; //容量
size_t _size; //有效字符的个数
const static int npos;
};
const int String::npos = -1;