1 结构体

1.1 结构体内存

按照成员变量中占用空间最大的来作为分配单位,同一个成员变量的存储空间不能跨分配单位,按顺序存储。空结构体也需要分配地址来区别空结构体的不同对象,大小为1字节。

1.1.1 内存对齐的原因

提高读写数据的效率,cpu访问地址是有规则的,只能按照对齐地址一个区间一个区间进行访问,如果没有字节对齐,那么一个数据可能就会跨越两个分配区间,这样就需要进行两次读写操作。

1.1.2 修改内存对齐的方法

#pragma pack(n)

1.1.3 计算内存例子

typedef struct   //分配单位4字节
{
   char a;   //分配一个4字节,占用1字节,空闲3字节
    int  b;  //空闲的3字节不够用,另外分配一个单位4字节
    char c;  //无空闲,再分配一个单位4字节
} debug_size1_t;
typedef struct
{
     char a;  //分配一个单位,剩余3字节空闲
    char b;  //使用空闲字节
    int  c; //空闲2字节不够用,再分配一个单位4字节
} debug_size2_t;

1.2 结构体和类的区别

  • c语言中区别:结构体中只有数据对象,不能有函数

  • c++中区别:结构体的默认访问权限是public,类的默认访问权限是private;类还可以使用类模板,结构体不行

    1.3 位域和共用体

    1.3.1 位域

    按二进制分配的数据结构,如下所示; data为bs变量,共占两个字节。按顺序存储,其中位域a占8位,位域b占2位,位域c占6位

    struct bs{
      int a:8;
      int b:2;
      int c:6;
    }data;

    1.3.2 共用体

    在相同位置存储不同类型数据,可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体变量所占的内存长度等于最长的成员变量的长度。作用:节省内存

    union Data{
     int i;
     float f;
     char str[20];
    } data;

    2 类

    2.1 类的基本概念

    2.1.1 类的定义

    所有的成员函数都必须在class主体内进行声明,至于定义,可以自由选择,若选择在class主体内进行定义,则这个函数被自动定义为内联函数。在class主体外定义则需要特殊的语法,需要使用类作用域解析运算符(::),且内联需要加inline。class定义(不是成员函数的定义)及其内联成员函数通常都会放在与class同名的头文件中,内联函数的特性决定了必须定义早于调用。

    2.1.2 类的访问权限

    按照类的访问权限来划分,可以将类的成员分为public、private、protected、fried四类

  • public:在类外部可访问

  • private:在类外部不可访问,只有在该类内部和类的友元函数可以访问

  • protected:在类外部不可访问,可以在本类及其子类,还有友元函数中访问

  • friend:可以访问本类内部成员(不包括子类),友元关系不能继承

    class Stack{
      public:
          //...public的实现部分
      private:
          //...private的实现部分  
          vector<string> _stack;//编程习惯:在data member前加下划线   
      protected:
          //...protected的实现部分
      friend void fun();//类的友元,可以是类或函数
    };

    2.1.3 类的基本组成

    通过观察一个空类中包含哪些成员,我们可以了解到类的基本组成,一个空类中包含了类的构造函数,拷贝构造函数,赋值运算符,取址运算符及析构函数。

    class Test1
    { 
    };
    // Test1等价于Test2
    class Test2
    {
    public:
       Test2()                               // 空的构造函数     
       Test2(Test2& t)                       // 拷贝构造函数(浅拷贝)     
       Test2& operator=(const Test2& other)  // 重载赋值操作符函数(浅拷贝)     
       Test2* operator & ();                 // 取址运算符     
       const Test2* operator & () const;     // 取址运算符const 
      ~Test2()                               // 析构函数
    };

    构造函数

  • 定义:即初始化函数,在类对象被定义出来时,编译器会自动调用适当的构造函数来对类对象进行实例化,构造函数有三个特征:(1)无返回类型;(2)函数名与类名相同;(3)可以被重载

  • explicit关键字:默认情况下类构造函数的都是隐式的(implicit),explicit可以将只含有一个参数的类构造函数声明为显式的,防止类构造函数的自动隐式转换
    Tri t5(10);
    Tri t5=10;//只含有一个参数时,可以这样隐式转换。一旦声明为显式,则这样写会报错
    Tri t5();//t5之后带一对(),t5会被视为一个函数(c的特性),参数为空,返回一个Tri对象的函数
    Tri t5;//ok,会调用无任何参数的构造函数

  • 初始化列表:用于类构造函数中显式地初始化类对象的成员,而普通的构造函数是以赋值的方式实现对成员的初始化,初始化列表的构造函数和内部赋值的构造函数对内置类型的成员没有什么大的区别;对于类对象类型的成员,内部赋值的构造函数在对该成员进行赋值之前,成员本身先被自己的构造函数初始化,再被赋值,效率不高;而使用初始化列表的构造函数则可以一步到位

    class A
    {
    public:
      int a;
      float b;
      A(): a(0),b(9.9) {} //构造函数初始化列表
    };
    class A
    {
    public:
      int a;
      float b;
      A()   //构造函数内部赋值
      {
          a = 0;
          b = 9.9;
      }
    };
  • 必须使用初始化列表的情况
    (1)const成员或引用类型的成员:因为const对象或引用类型只能初始化,不能对他们赋值
    (2)在子类的构造函数中初始化父类的私有成员

    析构函数

  • 定义:当对象结束生命时,会自动调用的函数,用来释放在对象生命周期中分配的资源,析构函数有三个定义规则:(1)“~”+“class名称”;(2)没有返回值;(3)没有参数
  • 使用说明:在class中以传值(by value)方式存放的成员,在类对象生命结束时自动被释放,不需要自定义析构函数,当对象含有指针类型的成员时,需要程序员自定义析构函数,对成员指针指向的内存空间进行释放,如下是一个使用的例子
    class Matrix{
      public:
          Matrix(int row,int col)
          :_row(row),_col(col)
          {
              _pmat=new double[row*col];    
          }
          ~Matrix(){
              delete [] _pmat;
          }
     private:
         int _row,_col;
         double * _pmat;
    }

2.1.4 类的特殊组成

static成员变量

  • 存放在全局数据区,用来表示唯一的、可共享的成员,它可以在同一类的所有对象中被访问。不在类对象的内存空间中
  • 使用注意事项
    (1)初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆
    (2)初始化时不加该成员的访问权限控制符private,public等
    (3)初始化格式,<数据类型><类名>::<静态数据成员名>=<值>

static成员函数

用来访问类的静态成员,不能访问类的非静态成员

const成员函数

  • 不会改变class对象的内容,const修饰符紧接于函数参数列表之后,必须同时在声明与定义中指定
    int length() const {return _length;}//const member function
  • mutable:为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰