C++最佳实践之常用库介绍

2023-05-16

C++的常用库包括:algorithm、chrono、iostream、future、memory、map、queue、unordered_map、regex、set、string、sstream、stdexcept、thread、vector、mutex等。熟悉这些C++库对我们开发有很大帮助,我们结合代码实践来介绍。

目录

一、algorithm算法

1、最小值与最大值

2、排序

3、二分查找

4、反转

5、替换

二、chrono时钟

三、iostream输入输出流

1、ios.h

2、istream.h

3、ostream.h

四、future异步任务

五、memory内存管理

六、map与unordered_map

七、queue队列

八、regex正则匹配

九、set集合

十、string字符串操作

十一、字符串格式化

十二、标准异常

十三、vector容器

十四、互斥锁

十五、thread线程库


一、algorithm算法

algorithm库包括:min、max、sort、binary_search、reverse、replace等函数。部分源码如下:

    template <class T>
    const T& min(const T& a, const T& b);

    template <class T>
    const T& max(const T& a, const T& b);

    template <class RandomAccessIterator>
    void sort(RandomAccessIterator first, RandomAccessIterator last);

    template <class ForwardIterator, class T>
    bool binary_search(ForwardIterator first, ForwardIterator last, const T& value);

    template <class BidirectionalIterator>
    void reverse(BidirectionalIterator first, BidirectionalIterator last);

    template <class ForwardIterator, class T>
    void replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value);

1、最小值与最大值

使用min()求两者最小值,用max()求两者最大值。示例如下:

    int a = 1, b = 2;
    int min = std::min(a, b);
    int max = std::max(a, b);

2、排序

使用sort()进行排序,接受的参数为iterator迭代器,示例如下:

    std::vector<int> array{3, 6, 1, 5, 9, 2, 8};
    std::sort(array.begin(), array.end());
    for (int it: array) {
        printf("%d ", it);
    }

3、二分查找

二分查找是基于有序数组使用二分法进行查找,如果找到返回true。示例如下:

bool result = std::binary_search(array.begin(), array.end(), 8);

4、反转

反转是把迭代器从头到尾反过来,比如一个升序数组反转后变成降序数组。示例如下:

std::reverse(array.begin(), array.end());

5、替换

 替换是遍历迭代器把就内容替换为新内容,示例如下:

std::replace(array.begin(), array.end(), 6, 666);

二、chrono时钟

在chrono库提供时间单位有:时、分、秒、毫秒、微秒、纳秒。如下表所示:

chrono::hours
chrono::minutes
chrono::seconds
chrono::milliseconds毫秒
chrono::microseconds微秒
chrono::nanoseconds纳秒

使用chrono可以获取日期、当前时间,也可以计算时间差。示例如下:

    using namespace std::chrono;
    system_clock::time_point time_point = system_clock::now();
    // 获取日期
    time_t time = system_clock::to_time_t(time_point);
    printf("date=%s", ctime(&time));
    // 获取当前时间,单位ms
    long time_millis = time_point.time_since_epoch().count() / 1000;
    printf("current millisecond=%ld", time_millis);
    // 计算时间差
    system_clock::time_point begin = system_clock::now();
    int sum;
    for (int i = 0; i < 10000; ++i) {
        sum += i;
    }
    system_clock::time_point end = system_clock::now();
    duration<double> diff = duration_cast<duration<double>>(end - begin);
    printf("use time=%lf", diff.count());

三、iostream输入输出流

输入输出流定义在iostream.h头文件中,内部包含ios.h、istream.h、ostream.h。而fstream.h提供打开和关闭文件的函数。

1、ios.h

ios头文件提供打开文件的模式,还有seek模式。其中打开文件模式如下表所示:

ios::app以追加方式打开
ios::ate文件打开后定位到文件尾
ios::binary以二进制方式打开
ios::in以输入方式打开(读)
ios::out以输出方式打开(写)
ios::nocreate不建立文件,文件不存在时打开失败
ios::noreplace不覆盖文件,文件存在时打开失败
ios::trunc如果文件存在,把文件长度设为0

seek模式包括:从头开始、当前位置和尾部开始,枚举定义如下:

enum seekdir {beg, cur, end};

2、istream.h

istream头文件提供输入流操作,源码定义如下:

    // 读一个字符
    basic_istream& get(char_type& c);
    // 读取一行
    basic_istream& getline(char_type* s, streamsize n);
    // 读取指定长度的内容
    basic_istream& read (char_type* s, streamsize n);
    // 读模式的seek
    basic_istream& seekg(off_type, ios_base::seekdir);

3、ostream.h

ostream头文件提供输出流操作,源码定义如下:

    // 写一个字符
    basic_ostream& put(char_type c);
    // 写入指定长度的内容
    basic_ostream& write(const char_type* s, streamsize n);
    // 刷新
    basic_ostream& flush();
    // 获取当前位置
    pos_type tellp();
    // 绝对位置的seek
    basic_ostream& seekp(pos_type);
    // 指定模式的seek
    basic_ostream& seekp(off_type, ios_base::seekdir);

四、future异步任务

future用于执行异步任务,等待执行结束后用get()获取结果。我们可以从packaged_task获取future,或者从async()获取future,也可以从promise获取future。示例如下:

    // 从packaged_task获取future
    std::packaged_task<int()> task([] { return 1; });
    std::future<int> f1 = task.get_future();
    std::thread t(std::move(task)); // 启动线程

    // 从async()获取future
    std::future<int> f2 = std::async(std::launch::async, [] { return 2; });
    // 从promise获取future
    std::promise<int> p;
    std::future<int> f3 = p.get_future();
    std::thread([&p] { p.set_value_at_thread_exit(3); }).detach();

    f1.wait();
    f2.wait();
    f3.wait();
    t.join();
    printf("f1=%d, f2=%d, f3=%d\n", f1.get(), f2.get(), f3.get());

五、memory内存管理

memory头文件提供四种智能指针:shared_ptr、unique_ptr、weak_ptr和auto_ptr。其中auto_ptr已经过时。智能指针的特性对比如下:

shared_ptr共享指针,使用引用计数
unique_ptr单一指针,一般用在单例场景
weak_ptr弱指针,对共享指针进行观察

另外提供allocator分配器,使用示例如下:

    std::allocator<int> allocator;
    int size = 3;
    // 分配内存块
    int *ptr = allocator.allocate(size);
    // 给每个内存地址赋值
    allocator.construct(ptr, 1);
    allocator.construct(ptr + 1, 2);
    allocator.construct(ptr + 2, 3);
    for (int i = 0; i < size; i++) {
        printf("alloc=%d", *(ptr + i));
        // 释放对应的内存地址
        allocator.destroy(ptr + i);
    }
    // 释放内存块
    allocator.deallocate(ptr, size);

六、map与unordered_map

C++提供map和unordered_map两种数据结构。其中map基于红黑树,unordered_map基于哈希表。优缺点对比如下:

优点缺点
map基于红黑树有序,操作时间复杂度lgn保存父节点和子节点,空间复杂度高
unordered_map基于哈希查找效率高无序,建立哈希表耗时

 map的操作示例如下:

    std::map<int, std::string> map;
    // add
    map.insert(std::pair<int, std::string>(1, "ferrari"));
    map.insert(std::pair<int, std::string>(2, "lanbojini"));
    map.insert(std::pair<int, std::string>(3, "rollsroyce"));
    map.insert(std::pair<int, std::string>(6, "benzi"));
    // remove
    map.erase(6);
    // 遍历
    for (auto it = map.begin(); it != map.end(); it++) {
        printf("key=%d, value=%s", it->first, it->second.c_str());
    }
    // find
    auto it = map.find(3);
    if (it != map.end()) {
        printf("found value=%s", it->second.c_str());
    }

七、queue队列

queue队列是FIFO先进先出,与queue相反的是stack,属于LIFO后进先出。我们来看看队列的操作示例:

    std::queue<int> queue;
    // 入队
    queue.push(111);
    queue.push(222);
    queue.push(333);
    printf("queue front=%d, back=%d", queue.front(), queue.back());
    // 出队
    while (!queue.empty()) {
        int front = queue.front();
        queue.pop();
    }

八、regex正则匹配

regex库是C++提供的正则匹配。示例如下:

    // 匹配规则
    std::regex regular(".{5},\\d{4}");
    std::string str_in("hello,2022");
    // 匹配结果
    std::smatch result;
    // 调用正则匹配
    if (std::regex_match(str_in, result, regular)) {
        for (int i = 0; i < result.size(); ++i) {
            LOGE("match=%s", result[i].first);
        }
    }

九、set集合

set是不重复key的集合,保证key的唯一性。示例如下:

    std::set<std::string> set;
    set.insert("hello");
    set.insert("world");
    printf("size=%lu\n", set.size());
    if (auto it = set.find("hello") != set.end()) {
        printf("find result=%d\n", it);
    }
    set.erase("hello");
    set.clear();

十、string字符串操作

字符串操作包括:拼接、删除、截取、替换、判断是否相等。使用示例如下:

    std::string str("hello");
    // 后面追加
    str.append(" world");
    // 前面插入
    str.insert(0, "ok,");
    // 截取子字符串
    str = str.substr(3);
    printf("sub str=%s\n", str.c_str());
    // 空格替换为逗号
    for (int i = 0; i < str.size(); ++i) {
        char ch = str.at(i);
        if (ch == ' ') {
            str.replace(i, 1, 1, ',');
        }
    }
    // 后面添加字符
    str.push_back('!');
    // 删除指定位置的字符
    str.erase(str.size() - 1);
    size_t pos = str.find("world");
    printf("find pos=%ld\n", pos);
    // 判断字符串是否相等,==属于操作符重载
    if (str == "hello,world") {
        // ...
    }

十一、字符串格式化

在C语言中,我们可以使用sprintf()函数进行字符串格式化输出,整型用%d,浮点型用%f,长整型用%ld,字符串用%s。在Java语言中,可以使用StringFormat进行格式化。今天的主角是C++的sstream库,提供stringstream进行字符串格式化。示例如下:

    // 字符串格式化
    std::stringstream stream;
    int a = 10;
    float b = 3.5;
    long c = 666;
    stream << "a=" << a << ", b=" << b << ", c=" << c;
    printf("stream:%s", stream.str().c_str());

十二、标准异常

C++在stdexcept库提供标准异常。基类是exception,位于exception.h,源码如下:

    class exception
    {
    public:
        exception() _NOEXCEPT {}
        virtual ~exception() _NOEXCEPT;
        virtual const char* what() const _NOEXCEPT;
    };

继承exception的类如下表所示:

bad_exception破坏异常,位于exception.h
bad_alloc分配异常,位于new.h
bad_cast转换异常,位于typeinfo.h
logic_error逻辑错误,位于stdexcept.h
runtime_error运行时错误,位于stdexcept.h

 继承logic_error的类如下表所示:

out_of_range数组越界
length_error长度错误
invalid_argument无效参数
domain_error域错误

继承runtime_error的类如下表所示:

range_error边界错误
overflow_error内存上溢
underflow_error内存下溢

十三、vector容器

vector是一个容器,基于模板类,理论上可支持任意类型。与数组区别是,vector可以动态扩容。另外,vector有begin()和end()迭代器。相关操作示例如下:

    std::vector<int> vector;
    // 尾部压入
    vector.push_back(2);
    // 指定位置插入
    vector.insert(vector.begin(), 1);
    vector.push_back(3);
    // 迭代器遍历
    for (auto it = vector.begin(); it != vector.end(); it++) {
        printf("val=%d\n", *it);
    }
    // 获取头部和尾部数值
    int front = vector.front();
    int back  = vector.back();
    // 移除首位的数值
    vector.erase(vector.begin());
    // 尾部弹出
    vector.pop_back();
    // 清空容器
    vector.clear();

十四、互斥锁

C++提供的互斥锁有:lock_guard、unique_lock、shared_lock和scoped_lock。对比如下:

lock_guard互斥锁包装器,构造时上锁,析构时解锁
unique_lock单一锁,可手动释放锁,锁的粒度更细
shared_lock共享锁,以共享模式锁住互斥,c++14
scoped_lock作用域锁,接受多个mutex,c++17

十五、thread线程库

C++的线程库是对pthread的封装,可以使用join启动,也可以使用detach启动。其中,join会让主线程等待子线程执行结束;而detach是把子线程分离出来,与主线程互不影响。下面是两个线程交替打印数字,模拟多线程同步的示例:

bool flag;
std::mutex mtx;
std::condition_variable cond;

void task1() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cond.wait(lock, []() {return flag;});
        if (i % 2 == 0) {
            printf("%s, count=%d\n", __func__, i);
        }
        flag = !flag;
        cond.notify_one();
        lock.unlock();
    }
}

void task2() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cond.wait(lock, []() {return !flag;});
        if (i % 2 == 1) {
            printf("%s, count=%d\n", __FUNCTION__, i);
        }
        flag = !flag;
        cond.notify_one();
        lock.unlock();
    }
}

然后是分别创建启动两个打印线程,奇偶交替打印输出:

    std::thread thread1(task1);
    std::thread thread2(task2);
    thread1.detach();
    thread2.detach();

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++最佳实践之常用库介绍 的相关文章

  • 在OpenCV中将cv::Mat绘制到MFC的视图中

    毕设时遇到了一个问题 xff0c 不能在MFC中显示由GrabCut抠出来的图 经一番折腾发现 xff1a 在OpenCV中如果图像最初是Mat类型的话 xff0c 就不能通过转换为IplImage xff0c 再转换为CvvImage来显
  • 【AutoLisp】AutoLisp中的DCL界面应用基础

    目录 01 DCL的作用 01 01 DCL介绍 01 02 DCL对话框成员 01 标准DCL对象家族 02 装饰DCL对象家族 03 集群DCL对象家族 04 整合DCL对象家族 02 DCL的加载 03 DCL的规则 04 DCL对话
  • 在MFC的picture控件中如何显示Mat图

    首先 xff0c 要进行输入检查 xff0c 看Mat数据是否是有效的 xff0c 如下图中所示 定义位图数据结构 xff0c 用以方便在图形设备接口 GraphicsDeviceInterface 上显示 xff0c 也就是windows
  • MFC+OPENCV+显示MAT类型图像

    MFC显示图像到界面 xff0c 可以用链接中的DrawMatToPic xff0c 有时会出现IplImage 类型转换问题 xff0c 因为用opencv做后续图像处理 xff0c 所以统一使用Mat类型 xff0c 可以showMat
  • CvMat、Mat、IplImage之间的转换详解及实例

    IplImage xff1a 在OpenCV中IplImage是表示一个图像的结构体 xff0c 也是从OpenCV1 0到目前最为重要的一个结构 xff1b 在之前的图像表示用IplImage xff0c 而且之前的OpenCV是用C语言
  • RGB和RGBQUAD的区别

    RGB和RGBQUAD的区别 昨天的晚上 为一个问题困扰了很长时间 几乎整个晚上都在弄一个小bug 感觉没有问题 但就是效果不理想 几次三番 查来查去 我想实现的功能是 生成一张图 图上有字 这张图是以一张指定的位图为背景的 我使用 COL
  • BITMAPINFO结构

    BITMAPINFO结构 BITMAPINFO结构具有如下形式 xff1a typedef struct tagBITMAPINFO BITMAPINFOHEADER bmiHeader RGBQUAD bmiColors 1 BITMAP
  • OpenCV中的cv::String和CString互相转换

    请注意是cv String xff0c 而不是std string xff0c 第一个字母是大写的 基本上CString转cv String网上都能查到 xff0c 而cv String转CString没有人提到 1 CString gt
  • MFC——文件打开和保存对话框(CFileDialog)

    最近要做一个文件打开和保存的对话框 xff0c 现将相关的代码记录如下 xff0c 用以备忘 xff01 所用控件 xff1a 2个静态标签 Static Text xff1a 用以显示功能标签 xff1b 2个文本框 Edit xff1a
  • OpenCv2 学习笔记(1) Mat创建、复制、释放

    OpenCV和VS2013的安装图文教程网上有很多 xff0c 建议安装好之后 xff0c 用VS2013建立一个空工程 xff0c 用属性管理器分别新建一个对应debug和release工程的props配置文件 xff0c 以后直接根据工
  • MFC C++ Cstring与string互转

    CString 转换成string 我试了很多的方法 xff0c 都不行 xff0c 我用的vs2010 解决方案 unicode CString sz1 61 L 34 abc 34 std string sz2 61 CT2A sz1
  • MFC+opencv 显示mat图像

    VS2015 43 opencv3 0 MFC显示图片中方法三在使用时 xff0c 只能显示彩色图像 xff0c 灰度图像显示有问题 xff0c 经查找 xff0c 是没有设置图像调色板的原因图片控件宽度不为4的倍数 显示错误 xff0c
  • 怎么去掉SVN前面的标签,如感叹号!

    1 问题陈述 xff1a 有时不小心将整个目录都检入 xff0c 导致整个页面的文件与目录都有svn的标签 xff0c 感叹号什么的 2 解决方法 xff1a 打开 所有程序 xff0c 找到TortoiseSVN gt Setting 修
  • 人工智能6.1 -- 机器学习算法篇(一)数据清洗、回归(含实践)

    人工智能 python xff0c 大数据 xff0c 机器学习 xff0c 深度学习 xff0c 计算机视觉 六 机器学习算法篇 xff08 一 xff09 数据清洗 回归 xff08 含实践 xff09 前言 目录算法热身结论 xff1
  • Tesseract-ocr 3.0.2源码 + VS2010项目工程 + 简单测试代码

    编译环境 Visual Studio 2010 所用类库版本 zlib 1 2 7 lpng1514 jpegsr9 tiff 4 0 3 giflib 5 0 4 leptonica 1 69 tesseract ocr3 0 2 下载地
  • Asprise OCR SDK 15.3试用版破解

    1 序言 之前因同事需要 xff0c 破解过Asprise OCR 4 0试用版本 xff0c 对这个库比较有印象 目前最新版本为15 3 xff0c 网上已经能下载到它的试用破解版本 xff0c 但似乎没有看到此版本的破解文章 Aspri
  • 内存中绘图 Memdc

    内存中绘图 Memdc CDC MemDC 首先定义一个显示设备对象 xff0c 所有的绘制首先绘制到这块内存中 CBitmap MemBitmap 定义一个位图对象 随后建立与屏幕显示兼容的内存显示设备 MemDC CreateCompa
  • MFC中char*,string和CString之间的转换

    string是使用STL时必不可少的类型 xff0c 所以是做工程时必须熟练掌握的 xff1b char 是从学习C语言开始就已经和我们形影不离的了 xff0c 有许多API都是以char 作为参数输入的 所以熟练掌握三者之间的转换十分必要
  • C++ 创建文件夹的四种方式

    在开头不得不吐槽一下 xff0c 我要的是简单明了的创建文件夹的方式 xff0c 看得那些文章给的都是复杂吧唧的一大坨代码 xff0c 不仔细看鬼知道写的是啥 因此 xff0c 为了方便以后自己阅读 xff0c 这里自己写一下 C 43 4

随机推荐

  • c++ 多线程:线程句柄可以提前关闭,但是线程并没有关闭

    很多程序在创建线程都这样写的 xff1a ThreadHandle 61 CreateThread NULL 0 CloseHandel ThreadHandle 1 xff0c 线程和线程句柄 xff08 Handle xff09 不是一
  • MFC的GDI绘制坐标问题

    MoveWindow和CDC的位置不一样 xff0c MoveWindow 起点坐标 xff0c 宽 xff0c 高 xff0c CDC xff1a 起点坐标 xff0c 终点坐标
  • C#线程同步(1)- 临界区&Lock

    文章原始出处 http xxinside blogbus com logs 46441956 html 预备知识 xff1a 线程的相关概念和知识 xff0c 有多线程编码的初步经验 一个机会 xff0c 索性把线程同步的问题在C 里面的东
  • 线程锁的概念函数EnterCriticalSection和LeaveCriticalSection的用法

    线程锁的概念函数EnterCriticalSection和LeaveCriticalSection的用法 注 xff1a 使用结构CRITICAL SECTION 需加入头文件 include afxmt h 定义一个全局的锁 CRITIC
  • C# 获取鼠标相对当前窗口坐标的方法

    编写客户端应用程序时 xff0c 经常要用到鼠标当前的位置 在C 的winform开发中 xff0c 可以用Control MousePosition获得当前鼠标的坐标 xff0c 使用PointToClient计算鼠标相对于某个控件的坐标
  • 【Linux】Rocky Linux 9.0 Podman服务无法正常启动

    Rocky Linux 9 0发布后 xff0c 我在本地虚拟机对该版本进行了安装和测试 xff0c 发现Podman服务在某些情况下 xff0c 无法正常启动 当 etc selinux config配置中 xff0c SELINUX 6
  • 如何在C#控件中画点并获得指定点的像素颜色

    画点的方法 方法一 用picGraphics FillRectangle new SolidBrush fillColor p X p Y 1 1 即用一个像素填充方法 方法二 用gdi32 dll库中的SetPixel方法 DllImpo
  • LinearLayout(线性布局)

    本节引言 本节开始讲Android中的布局 xff0c Android中有六大布局 分别是 LinearLayout 线性布局 xff0c RelativeLayout 相对布局 xff0c TableLayout 表格布局 FrameLa
  • 使用DockerFile创建ROS环境(带有xfce和vnc可以访问桌面查看ROS的图形工具)

    基于 consol ubuntu xfce vnc的DockerFile FROM consol ubuntu xfce vnc 切换到root xff0c root才有权限进行安装软件等操作 USER 0 替换桌面背景 xff08 Doc
  • ROS自定义消息类型与使用

    1 创建消息文件 在功能包中创建msg文件夹并在文件夹中创建消息文件exmsage msg Header header int32 demo int float64 demo double 2 修改package xml lt build
  • ROS中的订阅模式、服务模式、action模式

    在ROS的通信方式中存在订阅 发布模式 xff0c 服务模式 xff0c 动作服务模式 1 订阅 发布模式 使用订阅 发布模式进行通信 xff0c 首先要知道主题的存在 发布者向主题发布消息 xff0c 订阅者订阅主题获取消息 其中订阅者不
  • Shell获取标准错误并赋值给变量

    今天在写shell的过程中遇到个问题 xff0c 用 XXX 61 ln s XXX XXX 想在运行软链接时候把错误输出赋值给一个变量 xff0c 却怎么也赋值不了 xff0c 最后发现应该是标准输出和标准错误的问题 一般来说 只会得到命
  • C语言提高

    头文件函数声明 防止头文件重复包含 xff08 相互包含陷入包含死循环 xff09 pragma once 兼容C 43 43 编译器 如果是C 43 43 编译器 xff0c 按c标准编译 ifdefine cplusplus C语言编译
  • Python 实现文本文件多路归并排序

    开发说明 xff1a 前两天刚开始学习Python xff0c 但是厌烦了Hello World这类没用的东东 xff0c 于是就自己边学边做 xff0c 花了一天时间完成了这个稍微复杂的小应用 xff1b p 麻雀虽小五脏俱全 xff0c
  • C++ split基本操作

    源代码 xff1a void split const string amp s char delim vector lt string gt amp elems stringstream ss s string item while get
  • VSLAM学习之(一)

    相关资料整理 CVPR 2014 Visual SLAM Tutorial 学习SLAM需要哪些预备知识 VSLAM简介 VSLAM xff08 Visual Simultaneous Localization and Mapping xf
  • 一本书,让我走上编程之路

    好多年了 xff0c 我的书架上一直留着这本书 xff0c 不是因为有多好 xff0c 而是它让我明白了许多 工作后的我读过无数本厚厚薄薄的书 xff0c 其中有的确实十分精彩 但唯一让我真正用心读过的 xff0c 是大学期间一本普通的c
  • ubuntu虚拟机终端(terminal)打不开

    最近想用cmake gui编译opencv xff0c 发现虚拟机上终端 xff08 terminal xff09 打不开 xff0c 点图标也打不开 xff0c ctrl 43 alt 43 t也没反应 然后百度了一下ctrl 43 al
  • Debian 用户名密码输入成功后重复登录

    问题描述 xff1a 在用户名和密码都输入成功的情况下 xff0c 重复出现登录界面 xff0c 无法进入主界面 问题原因 xff1a 原因为修改了 etc environment 环境变量 xff0c 在文件末尾添加了 xff1a PAT
  • C++最佳实践之常用库介绍

    C 43 43 的常用库包括 xff1a algorithm chrono iostream future memory map queue unordered map regex set string sstream stdexcept