前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重温C++的设计思想

重温C++的设计思想

原创
作者头像
mariolu
发布2020-09-26 23:04:43
1.6K0
发布2020-09-26 23:04:43
举报

一、内存管理(RAII)

内存管理分为堆、栈和RAII(Resource Acquisition Is Initialization)。除了C,还有几个语言D、Ada和RAII少数派语言也采用RAII

  • RAII依托栈和析构函数,对包括堆内存的资源进行管理,所以不需要GC垃圾回收。
  • 对象很大、对象编译器不能确定大小、对象是函数返回值,不能或不应该存储在栈上。
  • 操作1:内存分配;操作2:内存释放;操作3:内存垃圾收集。C++通常会做上面的操作1和2;JAVA会做上面的操作1和3,Python会做操作1、2、3
  • 栈上分配和释放,只需要移动一下栈指针,由于后进先出的执行过程,所以不可能出现内存碎片、

二、智能指针

C++简单类型称为POD(Plain Old Data),有构造和析构函数称为非POD。

栈展开(stack unwinding):在发生异常时对析构函数的调用。也就是说不管是否发生了异常,析构函数都会得到执行。

std的智能指针(std::unique_ptr,std::shared_ptr),使用智能指针目的之一是减少对象的拷贝:对超出作用域的对象进行释放。

2.1 unique和shared_ptr

一个unique_ptr只能独自拥有一个对象,而多个shared_ptr可以同时共享指针。

多个不同的shared_ptr不仅共享同一个对象,也需要共享计数。

2.2 左值和右值

左值:有标识符、可以取地址的。反之为右值。

常见的左值有:变量、函数、成员;返回左值的表达式(++x,x=1,cout<<''),字符串常量

常见的右值有:返回右值得表达式(x++,x+1,make_shared<int>(42)),非字符串的字面量(42,true)

左值引用(T&)和右值引用(T&&)。左值和右值首先是个值,所有对于指针,因为用值传递,不关心它是左值还是右值。

std::move(ptr)是个右值引用。等价于static_cast<smart_ptr<T>&&>(ptr)。

2.3 内存对象的局部性

C++的对象缺省为值语义。

代码语言:javascript
复制
Class A{
    B b_;
    C c_;
}

很多语言包括Java和Python会在A对象放B和C的指针。而C++则会把B和C的对象放在A的内存空间。既有优点也有缺点。

优点是保证了内存访问的局限性。局限性在现代处理器架构上是绝对有优势,缺点是复制对象的开销大大增加,所以C++需要移动语义,而Java里根本没有。

对象支持移动需要下列几步:

  • 对象有拷贝构造和移动构造(除非你只需要像unique_ptr只打算支持移动,不支持拷贝)
  • 对象有swap成员函数
  • 对象命名空间里,有一个全局的swap函数swap(T&lhs, T&rhs),这样方便自己和别人调用。
  • 实现operator=函数。

三、容器

3.1 连续内存的vector容器

vector保证强异常安全性,如果元素类型没有提供一个保证不抛异常的移动构造函数,vector使用拷贝构造函数。如果自定义类型拷贝构造代价较高,则使用移动构造函数,并标其为noexcept,或者只在容器中放置对象的智能指针。

C++11提供的emplace系列函数,是为了提升容器性能设计。emplace_back比push_back 少额外生成临时对象,少一次拷贝构造和一次析构。

现代处理器架构对连续内存访问速度比不连续内存访问速度快很多,所以vector的连续内存是他的优点。而为了保证连续性,vector的一个缺点是大小增长时导致的元素移动。所以如果有可能,尽可能使用reserve函数保留所需内存。

3.2 部分元素连续的双端队列daque

deque是双端队列(double-ended queue),但是和vector比,多了push_front,emplace_front,和pop_front函数,但是不提供data,capacity和reserve函数。因为deque的内存结构有点像hash桶,元素只是部分连续的,所以无法提供data函数。内存中元素大部分仍然连续,每一段存储大小相等,所以还是可以用数组下标的方式访问。index[i/chunk_size][i%chunk_size]

3.3 list、单向链表

list是双向链表,对比vector,优化插入和删除。

单向链表C++11里面叫forward_list。在元素大小较小的时候,他可以有效的节约内存。

3.4 关联容器

c++的关联容器(set,map,multiset,multimap)是有序的,而在别的语言通常是无序的。名字带multi的允许键重复。不带的不允许键重复。set和multiset只存放键,而map和multimap存放键值对。与序列容器相比,关联容器没有前、后的概念。但是提供insert、emplace等函数。关联容器有find、lower_bound、upper_bound等查找函数,返回是一个迭代器。

3.5 容器共性

容器的共性:容器类都有begin()和end()函数,大部分容器拥有sizie(),push_back()。不必集成一个共同的容器积累,便可以拥有通用地遍历一个容器的方法。

四、返回值优化

c++的返回值优化,对于非值类型,当返回值可能是子对象的情况,使用unique_ptr或shared_ptr,对于移动代价很高的对象,考虑分配在堆上,然后返回一个句柄(unique_ptr),或者从外部传递一个非const的对象引用。

在函数调用复用一个自动容量对象,作为引用传递。

五、标准泛型算法

c++的标准泛型算法:

  • sort:排序
  • reverse:反转
  • count:计数
  • find:查找
  • max:最大值
  • min:最小值
  • minmax:最小值和最大值
  • next_permutation:下一个排列
  • gcd:最大公约数
  • lcm:最小公约数

标准算法,一般会做一些约定,只要容器满足这种条件就可以使用这个算法。比如说sort:

参数满足随机访问迭代器,迭代器指向对象可以使用<比较大小,满足严格弱序关系。

迭代器指向的对象可以移动。

5.1 C++算法比C语言的优势

  • 他的性能比c语言的qsort更好,是因为编译器对比较操作做了内联,而c语言里面是通过一个额外的函数调用来实现。
  • c的qsort函数要求数组内容是可以按比特复制的,c++则要求迭代器执行的内容是可移动的。

六、其他

constexpr和const是编译期常量和运行期常量的意思

lambda表达式:以一对中括号开始,不需要说明返回值(类似auto)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、内存管理(RAII)
  • 二、智能指针
    • 2.1 unique和shared_ptr
      • 2.2 左值和右值
        • 2.3 内存对象的局部性
        • 三、容器
          • 3.1 连续内存的vector容器
            • 3.2 部分元素连续的双端队列daque
              • 3.3 list、单向链表
                • 3.4 关联容器
                  • 3.5 容器共性
                  • 四、返回值优化
                  • 五、标准泛型算法
                    • 5.1 C++算法比C语言的优势
                    • 六、其他
                    相关产品与服务
                    容器服务
                    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档