^

Young Anything I do that may help others, I'll post it here.

C++笔记

编码和二进制

  1. 十进制小数转为二进制,要不断地乘以2直到为整数:
  2. 3个二进制位为一个8进制位,4个二进制位为一个16进制位。
  3. 原码:用0,1来表示正负号。其缺点:1.零的表示不唯一,四则运算符号位要单独处理,运算规则复杂。
  4. 补码:0的表示唯一,符号位可以作为数值参与运算。一般只考虑负数的原码转换方式:
    • 原码 -> 反码 -> 补码:负数的原码最高符号位不变,其他位全部取反,最后整体加1。这个过程叫做求补,补码转回原码只需要再次求补即可。

C++ 变量类型

<font color=#CD5C5C> 变量其实只不过是程序可操作的存储区的名称。</font>C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。
每种数据类型的字节长度和具体编译器有关:
1
2

C++ 变量初始化

初始化不是复制,初始化的含义是创建变量时赋予起一个初始值,而赋值的含义是吧对象当前值擦除,而以一个新值来替代。–C++Primer P39

如果初始化时未赋值,那其中的值为内存中的不确定垃圾值。
浮点数在内存中是近似存储的,看是否相等就是比较浮点数差值是否足够小。

运算符优先级

算术(乘加移位) > 逻辑 > 条件 > 赋值

隐式类型转换

隐式类型转换

关键字 auto 和 decltype

  • auto:编译器通过初始值自动推断变量类型
  • decltype:定义一个变量j与某一表达式i的类型相同,但并不用i初始化。
    • decltype(i)j = 2; //j以2作为初始值,类型与i一致。

field, parameters, local variables

  • field: 字段,成员变量。声明在类的构造函数和函数之外。只要类的对象拥有被类声明的具体的成员变量。
  • parameters: 函数参数
  • local variables: 本地变量
  • this: 关键字this是类的隐藏参数,是一个指针,表示的是具体对象的内存地址。即&some_obj = this

类的声明

  • 类的声明一般放在.h文件里面,.h文件里不应该出现定义!只有声明允许出现在.h里面:

    • extern variables
    • function prototypes(函数名后有{}的就是定义,否则是声明)
    • class/struct declaration(类和结构没有定义,只有声明)
  • 类的声明一般包括成员变量和函数。

  • 类是虚的,抽象的,是概念,不是实体。只有实体对象才能拥有变量。而函数是属于类的。

  • #include something#是编译预处理指令,将所要include的内容直接放到该指令所处的位置。

    • #include "xx.h":在当前目录中去寻找
    • #include <xx.h>:在系统头文件目录中去寻找,同#include <xx>
  • 一般的编译步骤为:

    • 编译预处理指令 (得到.ii) —> 编译器(得到.s) —> 汇编器(得到.o) —> 链接(链接所有.o得出可执行的.out)
  • 类的声明在.cpp文件完成预编译时只能出现一次!所以如果有多个.h文件需要预编译进.cpp可能需要在.h中使用标准头文件结构

    #ifndef HEADER_FLAG
    #define HEADER_FLAG
    //type declaration here...
    #endif // macro define HEADER_FLAG Completed 
    

类的构造和析构

  • 对象在定义出来时编译器不会主动帮助把内存相应的存储位置清空,这个动作需要设计程序时自己进行。所以就需要构造函数。

  • 构造函数(constructor):帮助对象被创建出来时(可以同时传入一些参数来帮助初始化)就得到初始化;就像python里的__init__

  • 析构函数(destructor):释放掉内存空间中的对象数据,不能有参数。在对象被包含的的第一个scope大括号结束时,对象被析构掉。被相当于python里的__del__

  /*class head (.h)*/
  class Cat // declare
  {
      int weight;
  public:
      Cat(initialWeight); // prototypes(原型) constructor
      ~Cat(); // prototypes destructor
      void grow(int years);
      void shout();
  };
  /*class body (.cpp)*/
  Cat::Cat(int initialWeight) // define constructor
  {
      weight = initialWeight;
      printf("Cat::Cat()--this=%p\n",this);
  }

  Cat::~Cat() // define destructor
  {
      printf("Cat::Cat()--this=%p\n",this);
  }
  • default constructor : 无参数的构造函数;auto default constructor : 带默认参数的构造函数。在C++中,C语言中的struct的声明是允许有函数的:

    struct X{int i; float f; char c;};
    X x1 = {1, 2.2, 'c'};
    X x2 = { {1, 1.1, 'a'},{2, 2.2, 'b'} };
    
    struct Y {float f; int i = a; Y(int a);}; // 此处结构体Y的声明中有构造函数
    // Y(int a) 不是default constructor, 因为它有参数
    Y y1[] = {Y(1), Y(2), Y(3)}; // 正确
    Y y2[2] = {Y(1)}; // 错误!因为Y的对象生成必须使用参数, 数组中第二个默认的Y对象无法生成
    

动态构造对象

所谓动态构造对象,是操作运算符newdelete分配(分配后可用指针访问该内存地址)或删除相应对象的内存空间。

  • new运算符返回的是内存地址;
  • delete声明的时候定义了多个student *r = new Student[10];,delete的时候就要带上[]:delete[] r;先调用r内每个元素的析构,然后回收掉每个元素的空间;
  • delete之前必须有对应的new,每个对象只能使用一次delete
  • delete可以安全用在null pointer。

访问限制

  • 同一个类的对象之间可以访问private成员变量;
  • class的成员变量默认属性为private,struct默认的为public;
  • 尽量使用class;

初始化列表

  1. 类里面的所有成员变量都用initialzation list初始化:Student::Student(string s):name(s){}
  2. 父类的构造函数初始化也要放到initialzation list。

组合与继承

  1. 对象可以作为private的成员变量放到其他的对象之中。而继承是类的层面的构造。

  2. 父类的private内容可以使用,但不能被修改。

    class A{
        private:
            int i;
        protected:
        	void set(int ii){i = ii;}
        public:
            A():i(0){}
            ~A(){}
            void print(){}
    };
       
    class B : public A{
        public:
        void f(){
            set(20);//父类protected的(public的也可以),所以可以用
            i=30;//错了。父类private,所以不可以用
            print();
        }
    };
       
    int main() //client class
    {
        B b;
        b.set(10);//错了,因为是protected的,所以非A子类是不可以用的
        b.print();
    };
    

3.如果父类有overload函数(函数同名,但参数不同),那么子类再写同名函数,父类所有相关同名函数全部被屏蔽掉;其他OOP语言此时的子类函数就是对父类中该函数的override

const

  1. 必须让编译器在编译时知道const的初始值。
char * const q = "haha"; //指针q是const
*q = 'a'; //ok
q++;      //error

const char *p = "haha"; //(*p)这块内存空间表示的变量是const
char const *p = "haha"; //(*p)这块内存空间表示的变量是const
*p = 'a'; //error
p++;      //ok
  1. 类的函数不会修改成员变量的话,需要在.h和.cpp函数名的后面写上const

  2. 对象声明前加cosnt说明整个对象的成员变量无法被修改。

    class A{
        const int i;
    public:
        //const的成员变量必须在此处初始化列表中初始化
        //而且不能这样用:int array[i]!
        A():i(0){}
        void f() {} //== f(A* this)
        void f() const {}//== f(const A* this)也是一种overload
    };
       
    int main()
    {
        const A a;
        a.f();//执行这个: void f() const {}
        return 0;
    }
    

多态性

任何一个类如果有virtual函数,那这个类的对象就会比正常的大一点,该类的析构函数必须也是virtual的,不然析构对象的时候会调用其父类的析构函数

含有虚函数的类的对象在内存中,第一位存放的是virtual table,它指向的是内存另外一个地方虚函数表,虚函数是override自己的虚函数和没有override的父类的虚函数。

A a();
B b();//B是Ade subclass
a=b;//只有子类中的共有的数据部分会被复制,虚函数还是保留了a自己的
----------------------------------
A* a=new A();
B* b=new B();
a=b;//这时候a和b指向完全相同的B的对象
----------------------------------
void func(A* p)//指针可以提现多态,指针会找到应该使用的函数
{
    p->render();//render是A类及其子类中定义的虚函数
}    
B b;
func(&b);
    
void func(A& a)//引用也可以提现多态,引用其实也是指针
{
    a.render();//render是A类及其子类中定义的虚函数
}
B b;
func(b); 

静态

  1. 静态局部变量就是全局变量;

  2. 静态的成员函数内部使用的变量必须全部是静态的。
  3. 如果一个类有静态的成员变量声明,那么就要在一个CPP文件中进行定义。

拷贝构造

用对象来初始化构造对象。

class A
{
    //默认会自动有一个拷贝构造,会拷贝传入对象所有的成员变量!
    A(const A&a)
    {
        //blabla;
    }
}

A func(A a)//此处会拷贝构造一个函数内局部作用的A对象!
{
    return a;
}

拷贝构造和赋值运算符:

//以下是拷贝构造
A a=b;
A a(b);

//以下是赋值运算
a=b;
//赋值运算符的重载
class A{
public
    A& operator=(const A&){
    
}
}

运算符重载

Q

  1. null pointer?int *p(null pointer) = int 0;
  2. p=new int;
  3. 类有定义吗?

细节

  1. A a=1; == A a(1);

  2. 一切常数、字符和字符串都是右值; 简单理解左值就是有变量名。

  3. 类的成员函数:const func(int a) const{}最后这个const指的是this。