计算机 · 2021年5月22日 0

深入应用C++11笔记 第7章

C++11的其他特性

委托构造函数和继承构造函数

委托构造函数:在一个构造函数中可以调用另外一个构造函数,但是如果使用了委托构造函数就不能同时使用类成员初始化。

class class_c{
  class_c(int my_max) {...}
  class_c(int my_max, int my_min) : class_c(my_max) {...}
};

继承构造函数:派生类会隐藏基类同名函数。

struct Base {
  int x;
  double y;
  string s;

  Base(int i) :x(i), y(0){}
  Base(int i, double j) :x(i), y(j){}
  Base(int i, double j, const string& str) :x(i), y(j), s(str){}
};

struct Derived : Base {
};

int main() {
  Derived d(1, 2.5, "ok"); //错误,没有相应的构造函数
  return 0;
}

为了解决这个问题,要么是在派生类中重新定义一次所有的构造函数:

struct Derived : Base {
  Derived(int i) :Base(i) {}
  Derived(int i, double j) :Base(i, j) {}
  Derived(int i, double j, string& str) :Base(i, j, str) {]
};

要么通过using来声明使用基类的函数(不仅仅是构造函数):

struct Derived : Base {
  using Base::Base;
};

int main()
{
  int i = 1;
  double j = 1.23;
  Derived d(i);
  Derived d1(i, j);
  Derived d2(i, j, "");
  return 0;
}

原始字面量

final和override标识符

final限制某个类不能被继承或者某个虚函数不能被重写,并且这个关键字要放到类或者虚函数的后面:

     struct A
     {
       virtual void foo() final;
     };
     struct B final : A
     {
       void foo();
     };

override用来表明这个函数是要用来重写基类的虚函数的,防止不小心把重写虚函数搞成重载。这个关键字也必须要放在函数的后面:

     struct A
     {
       virtual void func(){}
     };
     struct D: A{
       void func() override {}
     };

内存对齐

为什么有内存对齐

  • 为了性能。由于内存硬件上的组织方式(具体看这篇介绍内存对齐的文章),从内存中读取数据时往往是一次性读取4字节、8字节,为了能够尽可能地提升读写内存的效率,于是有了内存对齐的概念。像一些SSE之类的指令对数据也有着对齐上的要求。
  • CPU寻址支持。比如在某些平台上如果你把int存放在不是4的倍数开头的地址,那么你去存取这个int的时候是会报Bus Error(SIGBUS)的。写过移动端sdk的人应该对此深有体会。在x86平台上则没有这种问题。

内存对齐具体要求

对于基本数据类型,这些数据的内存地址必须是其对齐长度的倍数:

Data Type32-bit (bytes)64-bit (bytes)
char11
short22
int44
long88
float44
double88
long long88
long double416
Any pointer48

对于结构体,其对齐长度是其成员里对齐长度最大的那一个。

为了照顾内存对齐,结构体的内部和末尾会产生padding。

比如intel这篇文档里的例子

  struct s1
  {
    char a;
    short a1;
    char b1;
    float b;
    int c;
    char e;
    double f;
  };
  struct s2
  {
    double f;
    float b;
    short a1;
    char a,b1,e;
  };

结构体s1要占用32个字节,内部有11个字节的padding,而成员相同的结构体s2只占用24个字节,只有末尾的3个字节的padding。

踩过内存对齐的哪些坑

  • 写移动端sdk的人经常会遇到没注意内存对齐,导致报SIGBUS的问题;
    为了写出更portable和健壮的代码,需要留意数据类型的对齐要求;
  • 因为忽略了内存对齐的影响,在手动计算结构体大小和成员变量的偏移量时经常出错;
    这种错误挺坑的,可能你本意是打算存取某个成员变量的,实际上却操作的是作为padding的无用数据,在序列化反序列化传递数据时就会遇到这种坑;
    解决办法就是要么牢记内存对齐的规则,要么请熟练使用sizeof和offsetof两个宏;

理解了内存对齐后可以怎样把程序写的更好

  • 当然是避免上面列出的那些坑;
  • 参照intel文档的那个例子,合理安排成员变量的顺序节约空间;
  • 对于某些结构体,手动设置其内存对齐长度,提升其在cache上的性能;

malloc与内存对齐

malloc函数返回值是一个按照max_align_t对齐的地址,这个max_align_t一般是8和16,按max_align_t对齐保证了所有基本数据类型都可以放在malloc返回地址开头的内存里;

c++11中提供的内存对齐相关工具

alignas

alignas(32) long long a = 0;

struct alignas(32) MyStruct {
};

alignas(int) char c;

alignas支持把alignment改大,不能支持改小。要改小需要使用#pragma pack或者C++11中等价的_Pragma:

_Pragma("pack(1)")
struct MyStruct {
  char a;
  int b;
  short c;
  long long d;
  char e;
};
_Pragma("pack()")

alignof

用于获取alignment。

这里还讲了一个sizeof…的用法,用于获取变参的个数。alignof存在类似的用法,用于获取变参的alignment。

std::alignment_of

template< class T >
struct alignment_of : std::integral_constant<
                          std::size_t,
                          alignof(T)
                       > {};

和alignof效果一样,通过::value获取alignment大小。

std::aligned_storage

template< std::size_t Len, std::size_t Align = /*default-alignment*/ >
struct aligned_storage;


template<std::size_t Len, std::size_t Align /* default alignment not implemented */>
struct aligned_storage {
    struct type {
        alignas(Align) unsigned char data[Len];
    };
};

用于声明一块长度为Len,按Align对齐的内存。配合placement new使用。

std::align

void* align( std::size_t alignment,
             std::size_t size,
             void*& ptr,
             std::size_t& space );

Given a pointer ptr to a buffer of size space, returns a pointer aligned by the specified alignment for size number of bytes and decreases space argument by the number of bytes used for alignment. The first aligned address is returned.

The function modifies the pointer only if it would be possible to fit the wanted number of bytes aligned by the given alignment into the buffer. If the buffer is too small, the function does nothing and returns nullptr.

C++11新增的便利算法

  • all_of、any_of和none_of
  • find_if_not
  • copy_if
  • iota
  • minmax_elemen
  • is_sorted和is_sorted_until