C++基础

来源:互联网 发布:淘宝口令转换 编辑:程序博客网 时间:2024/04/29 19:35

C++基础

一、基础概念

(一)C和C++

  1. 常变量和符号常量
    (1)符号常量:无数据类型,编译时自动替换字符串,内存中无以常量命名的储存单元
    (2)常变量:内存中有以它命名的储存单元,值不可改变,声明同时必赋值。
  2. 输入输出流:
    (1)可以连续输入输出
    (2)控制符
  3. 函数
    (1)inline内置函数:节省函数调动时间。适合规模小且平凡使用的函数。建议性指令,编译系统会做出判断。
    (2)重载:函数名相同,但是参数或函数体不同。核心:系统要能根据参数的差异(参数类型,个数,顺序)辨别函数。
    (3)模板:不约定函数类型和参数类型,只定义相同的函数体和参数个数。
  4. 变量
    (1)作用域:局部和全局
    (2)存储方式:静态、动态(形参,函数中定义的变量,函数调用保护现场和返回地址);
    存储类别:auto、static、register(建议性)、extern;static定义的外部变量(或函数)不可以被其它文件以extern的方式引用,只允许在本文件内使用。
  5. 指针
    (1) int (*p) (int, int) //定义指向函数的指针
    (2) int *p(int, int) //定义返回指针类型的函数
    (3) int func(int a, int (*p)(int, int)) //传递指针函数
    (4) int *p[4] //定义指针数组
    (5) int (*p)[4] //定义指向数组的指针
    (6) const int *p = &a; // 无法通过指针改变变量,但是指针的指向可以改变;常用于形参传递,保护原变量。
    (7) int * const p =&a // 常指针,指针指向不可以改变,但是可以通过指针改变变量的值
    (8)空指针:指向的数据类型不确定,过渡型的,需要转换才能被使用。
  6. 引用:主要用于函数参数传递,虚实结合。本质上使地址传递,提高效率不用单独开销。
    引用、指针、变量名是三种访问变量的常用手段。
  7. 结构体:Student *p = &stu;
    访问方式:stu.name; (*p).name; p->name
  8. typedef便于程序移植时统一更改数据类型。

(二)C++面向对象

  1. 面向对象:抽象(归纳有关事物的共性)、封装、继承(软件重用)、多态(不同派生类对同一消息的不同响应)
  2. 思想:分块设计、组装功能
  3. 对象=属性(数据,封装起来)+行为(函数,对外的接口),对象间通过消息来联系;对象=算法+数据结构;程序=对象s+消息
  4. 设计考虑两方面:(1)如何设计对象,选择把哪些数据和操作封装在一起;(2)如何向对象发消息。
  5. 面向对象软件开发:OOA->OOD->OOP->OOT->OOSM
  6. 成员访问限定:
    (1)private(默认):私有成员只能被类中成员函数引用,类外不能引用(除友元类)
    (2)protected:不能被类外引用,但可以被派生类引用
  7. 成员函数
    (1)类外定义时,要用“::”作用域限定符 e.g. void Student::display()
    (2)良好的习惯:类内声明,类外定义。
    (3)成员函数存储方式:同一类的不同对象,数据分开储存,函数占用相同的地址,通过this指针来区别不同的对象。函数不占用对象的存储空间
  8. 类的封装和信息隐蔽:
    (1)公用接口和私有实现分离:用户可以接触公用接口,但是不能接触被隐蔽的数据和实现的细节。便于类的修改(一改全改)和调试(有针对性)。
    (2)类声明和成员函数分离:类声明文件.h、类实现文件.cpp(一次性编译成目标文件,避免反复多次重复编译)、主函数文件.cpp

二、核心

(一)类和对象

  1. 构造函数——初始化
    (1)名称必须与类名相同;
    (2)定义对象时系统自动调用,不能被用户调用,只能执行一次;
    (3)不具有任何类型,不返回任何值;
    (4) 可以重载,可以使用默认值(必须是声明时使用,因为这样用户可以看到)。
  2. 析构函数——清理工作
    (1)名称是类名前面加~
    (2)在函数中定义对象,函数结束,对象释放时调用;static局部对象在main或exit函数结束程序时调用;由new创建,当delete释放该对象时调用。
    (3)不具有任何类型,不返回任何值,没有参数
    (4)顺序:先构造,后析构
  3. 指向对象的指针
    (1)指向对象数据成员的指针=普通指针
    (2)指向对象成员函数的指针:
    声明: void(Time:: *p)(int, int,int)
    赋值: p = &Time::set_time(int, int,int);
    因为成员函数是属于整个类的而不是某个对象的,所以要说明所属类
    (3)this指针:值为当前被调用的成员函数所在的对象的起始地址,指向本类对象的指针;隐式使用,作为参数传递给成员函数。
  4. 常对象——数据保护
    (1)常数据成员:const int hour;
    值不可以改变,只能通过构造函数的参数初始化表进行初始化。
    Time::Time(int s, int m, int h): second(s), minute(m), hour(h){函数体}
    可以被任意成员函数引用。
    常对象的数据成员都是常数据成员。
    (2)常成员函数:void get_time() const;
    可以引用任意成员数据,但是不可以修改数据
    (3)常对象:Time const t1(12,34,46); const Time t1(12,34,46);
    常对象的数据成员都是常数据成员,成员函数则不一定。
    用户只能调用其常成员函数
    (4)常指针:Time * const ptr1 = &t1;指针的指向不变
    const Time * ptr1 = &t1;不能通过指针改变值
    指向非const型变量的指针不能指向const变量;指向const型变量的指针可以指向const变量和非const变量,但是都不能改变其值。
    (5)常引用:值不可以被改变
  5. 对象的赋值和复制
    书上的一句话:类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果
    Box b2 = b1; (b1已经定义)
  6. 静态成员——数据共享
    (1)静态数据成员:static int height;
    (2)该数据成员在所有对象的值是一样的;
    (3)只占一份空间,在程序开始时被分配,直到程序结束才释放;
    (4)默认赋值为零,只能在类外进行初始化:
    int Box::height = 10;
    和成员函数一样,它不属于某个单独的对象,而是属于一类。
    所以调用时,可以通过对象a.height也可通过类Box::height
    (5)全局变量也可以数据共享,但是不符合封装的要求。
    (6)静态成员函数:属于整个类,没有this指针,因而只能访问静态成员;
  7. 友元:数据共享——让非本类中的函数访问private数据,但是打破了封装性
    (1)声明:friend void display(Time &);
    (2)使用:void display(Time &t){ t.hour = 20} 与成员函数不同的是,要指出对象名t
    (3)友元类:类A中声明 friend B,则类B中所有成员都可以访问A的私有数据

(二)继承与派生(软件重用)

  1. 继承方式:解决派生类内、外对基类成员的访问权限,由基类成员的访问属性和继承方式共同决定
    (1)基类private(默认):派生类中不可访问
    (2)基类public(常用):派生类的访问属性=继承方式
    (3)基类protect:派生类的访问属性=max(protect, 继承方式),即取最高的权限
  2. 构造派生类三步:
    (1)从基类接受成员:全部接受,不可选择。构造函数和析构函数不能继承。
    (2)改变接受成员:访问属性,成员函数覆盖(同名,同参数个数、类型)
    (3)增加新成员
  3. 构造函数和析构函数
    (1)构造函数
    Student1(int n, string nam, char s, int a, string ad): Student(n, nam, s), monitor(nl, naml)
    对基类、子对象、派生类数据成员初始化。
    多层派生只初始化上一层派生类即可。
    (2)执行顺序:
    构造:基类->子对象->派生类
    析构:派生类->子对象->基类
  4. 多重继承(因为有二义性所以不提倡使用)
    (1)二义性:多个基类的数据成员重名情
    解决方法:派生类引用时制定作用域,如c1.A::display();
    缺点:占用不必要的空间
    (2)虚基类
    声明派生类时,指定继承方式:class B : virtual public A, 则派生类只继承该基类一次。(因为一个积累可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。)
    初始化:最后的派生类,要负责对直接基类和虚基类初始化。(避免虚基类的不同直接派生类初始化时产生分歧)
    使用的时候,直接引用变量名,不会产生歧义。
  5. 基类与派生类
    (1)公用派生类可以给基类赋值,给基类的引用赋值,可以给基类或基类的引用传参数,可以给指向基类的指针赋值
    (2)细节:通过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类增加的成员
    设Graduate类继承于Student类。Student * pt = &grad1; pt只是指向了grad1从Student类中继承的部分,pt->display()调用的是基类Student的函数!!
    (3)问题的解决:虚函数
  6. 类的组合
    (1)在一个类中,以另一个类的对象作为数据成员,成为类的组合。描述“有”的关系,是横向的。
    (2)继承描述“是”的关系,是纵向的。
  7. 继承的意义
    (1)有许多基类是被程序的其它部分或其它程序使用的,这些程序要求保留原有的基类不被破坏。 使用继承是建立新的数据类型,他继承了基类的所有特征, 但不改变基类本身。基类的名称、构成和访问属性丝毫没有改变,不会影响其他程序的使用。
    (2)用户往往得不到基类的源代码。如果想修改已有的类,必须掌握类的声明和类的实现(成员函数的定义)的源代码。但是,如果使用类库,用户是无法知道成员函数的代码的,因此也就无法对基类进行修改,保证了基类的安全。
    (3)在类库中,一个基类可能已被指定与用户所需的多种组件建立了某种联系,因此在类库中的基类是不容许修改的(即使用户知道了源代码,也决不允许修改)。
    (4)实际上,许多基类并不是从已有的其他程序中选取来的,而是专门作为基类设计的。有些基类可能并没有什么独立的功能,只是一个框架,或者说是抽象类。人们根据需要设计了一批能适用于不同用途的通用类,目的是建立通用的数据结构,以便用户在此基础上添加各种功能建立各种功能的派生类。
    (5)在面向对象程序设计中,需要设计类的层次结构,从最初的抽象类出发,每一层派生类的建立都逐步地向着目标的具体实现前进。换句话说,是不断地从抽象到具体的过程。每一层的派生和继承都要站在整个系统的角度统一规划,精心组织。

(三)多态性与虚函数

  1. 向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为。具体来说,就是具有不同功能的函数可以用同一个函数名
  2. 分类:静态(编译时决定:运算符、函数的重载)、动态(运行时决定:虚函数)
  3. 同名覆盖:两个同名函数,函数类型和参数个数都相同,分别在基类和派生类中,属于同名覆盖,不是重载。重载必须至少一个不同。
  4. 指向基类的指针,赋值为派生类时:如果不声明虚函数,则只能指向派生类中基类的部分;若声明虚函数,则可以指向该派生类的全部相关数据。
  5. 声明方法:在基类中virtual void display();
  6. 使用场合:基类中,且在派生过程中可能被修改;通过基类指针或引用去访问成员函数
  7. 虚析构函数:在基类定义析构函数时声明为虚函数,从而在delete撤销派生类时可以同时调用基类和派生类的析构函数。
  8. 纯虚函数:virtual float area() const = 0; 只声明虚函数,留在派生类中定义。
  9. 抽象类:不用于生成对象,用于派生。所有包含纯虚函数的类都是抽象类。

三、其它

(一)输入输出流


(二)运算符重载


(三)异常处理


(四)命名空间

0 0
原创粉丝点击