【c++丨STL】string模拟实现(附源码)

简介: 本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。

前言

       之前我们学习了STL的第一个容器--string及其常用接口的使用方法,不过仅仅掌握使用方法还不够,面试当中常常会让我们模拟实现STL的某个容器的关键框架。所以今天我们深入string底层,用我们的功底来模拟实现一个简单的string类。


       本篇博客我们不会将string的所有接口原模原样地实现出来,而是根据string的逻辑,模拟实现一些重点接口函数。


一、头文件(成员变量与函数声明)

       头文件中的内容是类成员以及我们将要实现的接口声明,代码如下:

#pragma once
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cstring>
using namespace std;
 
class String
{
public:
    //迭代器定义
    typedef char* iterator;
    typedef const char* const_iterator;
 
    //迭代器接口
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
 
    //常量成员
    static size_t npos;
 
    //全缺省构造
    String(const char* str = "");
 
    //拷贝构造
    String(const String& str);
 
    //赋值重载
    String& operator=(String s);
 
    //析构
    ~String();
 
    //获取只读字符串
    const char* c_str() const;
 
    //交换两字符串
    void swap(String& s);
 
    //清除字符串
    void clear();
 
    //容量接口
    size_t size() const;
    size_t capacity() const;
 
    //增容
    void reserve(size_t n);
 
    //下标引用重载
    char& operator[](size_t i);
    const char& operator[](size_t i) const;
 
    //插入
    void insert(size_t pos, char c);
    void insert(size_t pos, const char* str);
    void push_back(char c);
    void append(const char* str);
    String& operator+=(char c);
    String& operator+=(const char* str);
 
    //删除
    void erase(size_t pos, size_t len = npos);
 
    //查找
    size_t find(char c, size_t pos = 0);
    size_t find(const char* str, size_t pos = 0);
 
    //构造子串
    String substr(size_t pos = 0, size_t len = npos) const;
private:
    char* _str;//起始指针
    size_t _size;//字符串长度
    size_t _capacity;//空间大小
};
 
//compare
bool operator<(const String& l, const String& r);
bool operator>(const String& l, const String& r);
bool operator<=(const String& l, const String& r);
bool operator>=(const String& l, const String& r);
bool operator==(const String& l, const String& r);
bool operator!=(const String& l, const String& r);
 
//输入输出
ostream& operator<<(ostream& out, const String& str);
istream& operator>>(istream& in, String& str);
istream& getline(istream& in, String& str, char delim = '\n');
 
//交换两字符串--非成员函数版
void swap(String& s1, String& s2);


二、源文件(功能实现)

       对于上述声明的函数,我们会一一进行实现并分析代码。首先从交换函数开始:


交换两字符串

       之前我们就了解到string类中有两个交换函数,一个重载为成员函数,另一个是非成员函数。我们就将二者都实现一下:

//交换两字符串
void String::swap(String& s)
{
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capacity, s._capacity);
}
 
//交换两字符串--非成员函数版
void swap(String& s1, String& s2)
{
    s1.swap(s2);
}

交换函数非常简单,虽然两对象都有各自所维护的内存,但是我们只需要调用标准库中的交换函数交换两指针的指向即可。然后将_size和_capacity也交换一下。



对于非成员函数般的交换函数,直接调用成员函数即可。


为什么先要实现交换函数呢?待会我们实现默认成员函数时,你自然能体会到它的妙用。


构造函数

//全缺省构造
String::String(const char* str)
    :_size(strlen(str))
{
    _str = new char[_size + 1] {'\0'};
    _capacity = _size;
    strcpy(_str, str);
}

在构造函数当中,我们首先将元素个数调整为等同于str长度的值,便于将字符串中的内容拷贝给新对象,然后在起始指针处动态开辟内存(注意大小是_size + 1,因为最后一个位置存放 '\0' )。注意我们_capacity的大小是不包含 '\0' 的,但实际开辟的内存大小要大于它。最后,将str中的数据拷贝给_str就好。不传参时,str的内容默认设置为空字符串。



拷贝构造

//拷贝构造
String::String(const String& s)
{
    _str = new char[s._capacity + 1] {'\0'};
    strcpy(_str, s._str);
    _size = s._size;
    _capacity = s._capacity;
}

与构造函数的逻辑相同,先开辟空间,然后拷贝数据即可。不过这是传统写法,还有一个现代写法:

//拷贝构造 现代写法
String::String(const String& s)
{
    String tmp(s._str);
    swap(tmp);
}

仔细观察就会发现,这种写法非常巧妙:首先我们用s中的字符串构造临时对象tmp,然后用swap交换tmp与新对象的内容,这样就很轻松地完成了拷贝构造。



赋值重载

       与拷贝构造类似,赋值重载也有传统写法和现代写法:

//赋值重载 传统写法
String& String::operator=(const String& s)
{
    if (this != &s)//若是自己给自己赋值,就什么都不做
    {
        delete[] _str;
        _str = new char[s._capacity + 1] {'\0'};
        strcpy(_str, s._str);
        _size = s._size;
        _capacity = s._capacity;
    }
    return *this;//可以支持连续赋值
}
//赋值重载 现代写法
String& String::operator=(String s)
{
    swap(s);
    return *this;
}

在传统写法当中,我们老老实实地销毁原字符串,然后重新开辟空间,再将新字符串拷贝过来...非常麻烦。现代写法当中,我们使用传值传参,构造一份相同的对象s,然后直接交换s与this对象,this对象就被成功赋值,并且等号右边的对象也没有被改动。


析构函数

       由于我们的字符串是动态开辟的,所以就要显示写析构函数,在对象销毁时释放这些内存:

//析构
String::~String()
{
    delete[] _str;
    _str = nullptr;//及时制空
    _size = _capacity = 0;
}

这里不必多说,写法和c语言实现的顺序表没什么区别。接下来我们开始正式实现常量成员nops和一些比较常用的接口。


常量成员npos

常量成员定义如下:

//常量成员
size_t String::npos = -1;

常量成员用无符号整数的-1来表示,是size_t的最大值,它作为一些接口的缺省参数,用于表示“直到字符串结束”。


获取只读字符串

//获取只读字符串
const char* String::c_str() const
{
    return _str;
}

获取只读字符串时,直接返回字符串起始指针即可。注意返回值被const修饰,该字符串不能被外部修改。


清除字符串

//清除字符串
void String::clear()
{
    _str[0] = '\0';
    _size = 0;
}

清除字符串时,只需将第一个字符设为 '\0' ,然后调整长度为0即可,无需调整空间容量。


容量接口

       容量接口便于用户访问字符串的长度或空间容量。

//容量接口
size_t String::size() const
{
    return _size;
}
size_t String::capacity() const
{
    return _capacity;
}

增容

//增容
void String::reserve(size_t n)
{
    if (n > _capacity)//当需要空间大于原有容量时,才需要增容
    {
        char* tmp = new char[n + 1] {'\0'};
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

reserve的作用是为字符串预留空间,单位是字节,注意:当参数n的值小于已有空间的总大小时,不会改变其大小。当我们实现增容时,首先需要开辟一块n + 1字节的内存空间(最后位置放 '\0' ),然后将数据拷贝到新空间,再释放原来的空间。



       接下来,我们开始实现字符串元素访问与遍历相关操作。首先从下标引用开始:


下标引用重载

//下标引用重载
char& String::operator[](size_t i)
{
    assert(i < _size);//防止越界
    return _str[i];
}
const char& String::operator[](size_t i) const
{
    assert(i < _size);
    return _str[i];
}

注意:为了确保用户能够修改字符串的内容,函数返回值是字符的引用。对于const对象,内容不能修改,则返回const引用。


迭代器

       之前已经提到过,现阶段我们可以将迭代器理解成一种指针,通过解引用来访问数据元素。我们定义String类时,使用指针来模拟迭代器。迭代器定义如下:

//迭代器定义
typedef char* iterator;
typedef const char* const_iterator;
//迭代器接口
String::iterator String::begin()
{
    return _str;
}
String::iterator String::end()
{
    return _str + _size;
}
String::const_iterator String::begin() const
{
    return _str;
}
String::const_iterator String::end() const
{
    return _str + _size;
}


注意:我们实现的迭代器接口函数名只有和STL保持一致,才可以支持范围for。


让我们使用一下自己的迭代器:

int main()
{
    String str = "hello world";
 
    //迭代器遍历
    for (auto it = str.begin(); it != str.end(); it++)
    {
        cout << *it << ' ';
    }
    cout << endl;
 
    //范围for
    for (auto e : str)
    {
        cout << e << ' ';
    }
    cout << endl;
    return 0;
}



字符串插入和删除操作

       接下来,我们开始实现string字符串的插入和删除操作。


insert

       insert支持指定位置的插入操作,我们首先实现它,后续实现其他插入删除操作时就可以直接调用该接口。代码如下:

//插入
void String::insert(size_t pos, char c)//插入字符
{
    assert(pos < _size);//防止越界
    if (_size == _capacity)//空间不够则增容
    {
        //调用增容函数
        reserve(_capacity == 0 ? 4 : 2 * _capacity);//初始设置为4,后续二倍开辟
    }
 
    //pos之后的字符全部右移一位
    size_t end = _size + 1;
    while (end > pos)
    {
        _str[end] = _str[end - 1];
        end--;
    }
    _str[pos] = c;
    _size++;
}
void String::insert(size_t pos, const char* str)//插入字符串
{
    assert(pos <= _size);
    size_t len = strlen(str);
    if (_size + len > _capacity)//空间不够,增容
    {
        //先扩2倍,若还不够,则设置为等同于总长度的大小
        size_t newcapacity = _capacity * 2;
        if (newcapacity < _size + len)
        {
            newcapacity = _size + len;
        }
        reserve(newcapacity);
    }
 
    //pos之后的字符全部右移len位
    size_t end = _size + len;
    while (end > pos + len - 1)
    {
        _str[end] = _str[end - len];
        end--;
    }
    for (size_t i = 0; i < len; i++)
    {
        _str[pos + i] = str[i];
    }
    _size += len;
}


第一个函数实现的是字符的插入,第二个是字符串的插入。实现思路类似于顺序表指定位置的插入,这里不再赘述。接下来实现剩余的插入接口:


push_back、append、operator+=

void String::push_back(char c)
{
    insert(_size, c);
}
void String::append(const char* str)
{
    insert(_size, str);
}
String& String::operator+=(char c)
{
    insert(_size, c);
    return *this;
}
String& String::operator+=(const char* str)
{
    insert(_size, str);
    return *this;
}


这三个函数的功能是尾插字符或字符串,直接调用我们刚才实现的insert就可以实现。


erase

       erase支持指定位置的删除操作,代码如下:


//删除
void String::erase(size_t pos, size_t len)
{
    assert(pos < _size);
    if (pos + len >= _size)//删除的字符个数超出了现有个数,则直接删除pos后的所有字符
    {
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        //(pos + len)之后的元素全体左移len位
        size_t end = pos + len;
        while (end <= _size)
        {
            _str[end - len] = _str[end];
            end++;
        }
        _size -= len;
    }
}


这里的逻辑与顺序表指定位置删除类似,不再多说。


查找

       接下来,我们实现查找功能find:

//查找
size_t String::find(char c, size_t pos)//从pos位置开始查找
{
    assert(pos < _size);
    for (size_t i = 0; i < _size; i++)
    {
        if (_str[i] == c) 
        {
            return i;
        }
    }
    return npos;
}
size_t String::find(const char* str, size_t pos)
{
    assert(pos < _size);
    const char* p = strstr(_str + pos, str);//调用strstr查找子串
    if (p == nullptr)//没找到
    {
        return npos;
    }
    return p - str;//返回两指针相对位置
}


查找可分为字符查找和字符串查找,其中字符串查找调用strstr函数即可,注意返回值细节处理。


构造子串

       substr可以构造一个string类,内容是原字符串中pos位置开始的len个字符组成的子串。代码如下:

//构造子串
String String::substr(size_t pos = 0, size_t len) const
{
    assert(pos < _size);
    if (len > (_size - pos))//超出字符串范围,则默认取到尾
    {
        len = _size - pos;
    }
    String sub;
    for (size_t i = pos; i < len; i++)//循环尾插
    {
        sub += _str[pos + i];
    }
    return sub;
}

compare

       compare指的是一些字符串之间的比较函数,属于非成员函数,实现方法也很简单:

//compare
bool operator<(const String& l, const String& r)
{
    return strcmp(l.c_str(), r.c_str()) < 0;//调用strcmp比较
}
bool operator>(const String& l, const String& r)
{
    return !(l < r);
}
bool operator<=(const String& l, const String& r)
{
    return (l == r || l < r);
}
bool operator>=(const String& l, const String& r)
{
    return (l == r || l > r);
}
bool operator==(const String& l, const String& r)
{
    return strcmp(l.c_str(), r.c_str()) == 0;
}
bool operator!=(const String& l, const String& r)
{
    return !(l == r);
}


输入和输出

       最后要实现的就是输入和输出。它们可以让我们直接配合cin和cout去操作我们自己的string类。 注意这里的细节处理:

//输入输出
ostream& operator<<(ostream& out, const String& str)
{
    for (auto ch : str)//遍历打印每一个字符
    {
        out << ch;
    }
    return out;//支持连续输出
}
istream& operator>>(istream& in, String& str)
{
    str.clear();//先清除原字符串
    char ch = in.get();//使用get函数读取单个字符
    while (ch != ' ' && ch != '\n')//读取到空格或换行就停止
    {
        str += ch;
        ch = in.get();
    }
    return in;//支持连续输入
}

接下来我们再实现一个自定义读取结束符的getline函数:

istream& getline(istream& in, String& str, char delim)
{
    str.clear();
    char ch = in.get();
    while (ch != delim)
    {
        str += ch;
        ch = in.get();
    }
    return in;
}

三、程序全部代码

       我们模拟实现string类的全部代码如下:

#include <iostream>
#include <algorithm>
#include <cassert>
#include <cstring>
using namespace std;
 
class String
{
public:
    //迭代器定义
    typedef char* iterator;
    typedef const char* const_iterator;
 
    //迭代器接口
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
 
    //常量成员
    static size_t npos;
 
    //全缺省构造
    String(const char* str = "");
 
    //拷贝构造
    String(const String& str);
 
    //赋值重载
    String& operator=(String s);
 
    //析构
    ~String();
 
    //获取只读字符串
    const char* c_str() const;
 
    //交换两字符串
    void swap(String& s);
 
    //清除字符串
    void clear();
 
    //容量接口
    size_t size() const;
    size_t capacity() const;
 
    //增容
    void reserve(size_t n);
 
    //下标引用重载
    char& operator[](size_t i);
    const char& operator[](size_t i) const;
 
    //插入
    void insert(size_t pos, char c);
    void insert(size_t pos, const char* str);
    void push_back(char c);
    void append(const char* str);
    String& operator+=(char c);
    String& operator+=(const char* str);
 
    //删除
    void erase(size_t pos, size_t len = npos);
 
    //查找
    size_t find(char c, size_t pos = 0);
    size_t find(const char* str, size_t pos = 0);
 
    //构造子串
    String substr(size_t pos = 0, size_t len = npos) const;
private:
    char* _str;
    size_t _size;
    size_t _capacity;
};
 
//compare
bool operator<(const String& l, const String& r);
bool operator>(const String& l, const String& r);
bool operator<=(const String& l, const String& r);
bool operator>=(const String& l, const String& r);
bool operator==(const String& l, const String& r);
bool operator!=(const String& l, const String& r);
 
//输入输出
ostream& operator<<(ostream& out, const String& str);
istream& operator>>(istream& in, String& str);
istream& getline(istream& in, String& str, char delim = '\n');
 
//交换两字符串--非成员函数版
void swap(String& s1, String& s2);
 
//交换两字符串
void String::swap(String& s)
{
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capacity, s._capacity);
}
 
//交换两字符串--非成员函数版
void swap(String& s1, String& s2)
{
    s1.swap(s2);
}
 
//全缺省构造
String::String(const char* str)
    :_size(strlen(str))
{
    _str = new char[_size + 1] {'\0'};
    _capacity = _size;
    strcpy(_str, str);
}
 
拷贝构造 传统写法
//String::String(const String& s)
//{
//  _str = new char[s._capacity + 1] {'\0'};
//  strcpy(_str, s._str);
//  _size = s._size;
//  _capacity = s._capacity;
//}
 
//拷贝构造 现代写法
String::String(const String& s)
{
    String tmp(s._str);
    swap(tmp);
}
 
赋值重载 传统写法
//String& String::operator=(const String& s)
//{
//  if (this != &s)//若是自己给自己赋值,就什么都不做
//  {
//      delete[] _str;
//      _str = new char[s._capacity + 1] {'\0'};
//      strcpy(_str, s._str);
//      _size = s._size;
//      _capacity = s._capacity;
//  }
//  return *this;
//}
 
//赋值重载 现代写法
String& String::operator=(String s)
{
    swap(s);
    return *this;
}
 
//析构
String::~String()
{
    delete[] _str;
    _str = nullptr;//及时制空
    _size = _capacity = 0;
}
 
//常量成员
size_t String::npos = -1;
 
//获取只读字符串
const char* String::c_str() const
{
    return _str;
}
 
//清除字符串
void String::clear()
{
    _str[0] = '\0';
    _size = 0;
}
 
//容量接口
size_t String::size() const
{
    return _size;
}
size_t String::capacity() const
{
    return _capacity;
}
 
//增容
void String::reserve(size_t n)
{
    if (n > _capacity)//当需要空间大于原有容量时,才需要增容
    {
        char* tmp = new char[n + 1] {'\0'};
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}
 
//下标引用重载
char& String::operator[](size_t i)
{
    assert(i < _size);//防止越界
    return _str[i];
}
const char& String::operator[](size_t i) const
{
    assert(i < _size);
    return _str[i];
}
 
//迭代器接口
String::iterator String::begin()
{
    return _str;
}
String::iterator String::end()
{
    return _str + _size;
}
String::const_iterator String::begin() const
{
    return _str;
}
String::const_iterator String::end() const
{
    return _str + _size;
}
 
//插入
void String::insert(size_t pos, char c)//插入字符
{
    assert(pos < _size);//防止越界
    if (_size == _capacity)//空间不够则增容
    {
        //调用增容函数
        reserve(_capacity == 0 ? 4 : 2 * _capacity);//初始设置为4,后续二倍开辟
    }
 
    //pos之后的字符全部右移一位
    size_t end = _size + 1;
    while (end > pos)
    {
        _str[end] = _str[end - 1];
        end--;
    }
    _str[pos] = c;
    _size++;
}
void String::insert(size_t pos, const char* str)//插入字符串
{
    assert(pos <= _size);
    size_t len = strlen(str);
    if (_size + len > _capacity)//空间不够,增容
    {
        //先扩2倍,若还不够,则设置为等同于总长度的大小
        size_t newcapacity = _capacity * 2;
        if (newcapacity < _size + len)
        {
            newcapacity = _size + len;
        }
        reserve(newcapacity);
    }
 
    //pos之后的字符全部右移len位
    size_t end = _size + len;
    while (end > pos + len - 1)
    {
        _str[end] = _str[end - len];
        end--;
    }
    for (size_t i = 0; i < len; i++)
    {
        _str[pos + i] = str[i];
    }
    _size += len;
}
void String::push_back(char c)
{
    insert(_size, c);
}
void String::append(const char* str)
{
    insert(_size, str);
}
String& String::operator+=(char c)
{
    insert(_size, c);
    return *this;
}
String& String::operator+=(const char* str)
{
    insert(_size, str);
    return *this;
}
 
//删除
void String::erase(size_t pos, size_t len)
{
    assert(pos < _size);
    if (pos + len >= _size)//删除的字符个数超出了现有个数,则直接删除pos后的所有字符
    {
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        //(pos + len)之后的元素全体左移len位
        size_t end = pos + len;
        while (end <= _size)
        {
            _str[end - len] = _str[end];
            end++;
        }
        _size -= len;
    }
}
 
//查找
size_t String::find(char c, size_t pos)//从pos位置开始查找
{
    assert(pos < _size);
    for (size_t i = 0; i < _size; i++)
    {
        if (_str[i] == c) 
        {
            return i;
        }
    }
    return npos;
}
size_t String::find(const char* str, size_t pos)
{
    assert(pos < _size);
    const char* p = strstr(_str + pos, str);//调用strstr查找子串
    if (p == nullptr)//没找到
    {
        return npos;
    }
    return p - str;//返回两指针相对位置
}
 
//构造子串
String String::substr(size_t pos = 0, size_t len) const
{
    assert(pos < _size);
    if (len > (_size - pos))//超出字符串范围,则默认取到尾
    {
        len = _size - pos;
    }
    String sub;
    for (size_t i = pos; i < len; i++)//循环尾插
    {
        sub += _str[pos + i];
    }
    return sub;
}
 
//compare
bool operator<(const String& l, const String& r)
{
    return strcmp(l.c_str(), r.c_str()) < 0;//调用strcmp比较
}
bool operator>(const String& l, const String& r)
{
    return !(l < r);
}
bool operator<=(const String& l, const String& r)
{
    return (l == r || l < r);
}
bool operator>=(const String& l, const String& r)
{
    return (l == r || l > r);
}
bool operator==(const String& l, const String& r)
{
    return strcmp(l.c_str(), r.c_str()) == 0;
}
bool operator!=(const String& l, const String& r)
{
    return !(l == r);
}
 
//输入输出
ostream& operator<<(ostream& out, const String& str)
{
    for (auto ch : str)//遍历打印每一个字符
    {
        out << ch;
    }
    return out;//支持连续输出
}
istream& operator>>(istream& in, String& str)
{
    str.clear();//先清除原字符串
    char ch = in.get();//使用get函数读取单个字符
    while (ch != ' ' && ch != '\n')//读取到空格或换行就停止
    {
        str += ch;
        ch = in.get();
    }
    return in;//支持连续输入
}
istream& getline(istream& in, String& str, char delim)
{
    str.clear();
    char ch = in.get();
    while (ch != delim)
    {
        str += ch;
        ch = in.get();
    }
    return in;
}

总结

       今天,我们在学会使用string类的基础上模拟实现了string类的常用功能,这对于我们学习数据结构和string类有很大帮助。之后博主会和大家一起进入下一个容器——vector的学习。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

相关文章
|
7月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
176 2
|
8月前
|
存储 安全 C语言
C++ String揭秘:写高效代码的关键
在C++编程中,字符串操作是不可避免的一部分。从简单的字符串拼接到复杂的文本处理,C++的string类为开发者提供了一种更高效、灵活且安全的方式来管理和操作字符串。本文将从基础操作入手,逐步揭开C++ string类的奥秘,帮助你深入理解其内部机制,并学会如何在实际开发中充分发挥其性能和优势。
|
4月前
|
对象存储 C++ 容器
c++的string一键介绍
这篇文章旨在帮助读者回忆如何使用string,并提醒注意事项。它不是一篇详细的功能介绍,而是一篇润色文章。先展示重载函数,如果该函数一笔不可带过,就先展示英文原档(附带翻译),最后展示代码实现与举例可以直接去看英文文档,也可以看本篇文章,但是更建议去看英文原档。那么废话少说直接开始进行挨个介绍。
88 3
|
7月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
344 73
|
8月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
7月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
287 3
|
8月前
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
381 1
|
4月前
|
存储 编译器 C语言
关于string的‘\0‘与string,vector构造特点,反迭代器与迭代器类等的讨论
你真的了解string的'\0'么?你知道创建一个string a("abcddddddddddddddddddddddddd", 16);这样的string对象要创建多少个对象么?你知道string与vector进行扩容时进行了怎么的操作么?你知道怎么求Vector 最大 最小值 索引 位置么?
82 0
|
7月前
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
154 11
|
7月前
|
Java
课时14:Java数据类型划分(初见String类)
课时14介绍Java数据类型,重点初见String类。通过三个范例讲解:观察String型变量、&quot;+&quot;操作符的使用问题及转义字符的应用。String不是基本数据类型而是引用类型,但使用方式类似基本类型。课程涵盖字符串连接、数学运算与字符串混合使用时的注意事项以及常用转义字符的用法。
161 9