目录
1. C语言 基础知识概述
- 优先级部分:
- 数组下标[] () .(成员选择) ->(成员选择)
- 自右到左运算: - ~ ++ – *(指针变量) & ! (类型) sizeof
- 算数运算符 逻辑运算符&& ||
- switch() 参数只能是整型变量;
- while语句与do…while语句的不同在于:do…while先执行一次循环条件,再判断循环条件;
- break语句作用是跳出最近的内循环语句,或者终止switch中的case并跳出该switch结构;continue则是终止本次循环,并执行下一次循环;
- c语言中使用char数组来实现字符串;以数字0或’\0’结尾,若不是该结尾方式,则是普通的字符数组;
- 字符串处理函数:
- gets()和scanf()无法知道字符串的大小,且易越界;fgets()避免了这些问题;
- puts()在输出完成后,自动输出’\n’;而 fputs()不会输出;
- strlen()计算字符串长度不包含结束符’\0’; sizeof() 计算时包含’\0’;
- strcpy(char *dest, const char *src),结束符也将被拷贝;strncpy()只拷贝指定长度;
- strncat()只是将src的前n个字符连接到dst上,后面追加’\0’;
- sprintf(),sscanf()涉及转换及格式化数据;
- strchr()用于查找字符串中的字符,并返回第一次出现的地址;而strstr查找的是字符串的地址;
- 子函数中使用return代表子函数结束,调用exit()表示程序终止;
- 避免同一个文件被多次include:
- 使用 #ifndef #define #endif
- 使用 #pragma once
- NULL是一个值为0的宏常量;
- 指针:
- const 指针具有左定值,右定向的功能;当const (指针类型) (指针变量)时,指针指向的值保持不变;当 (指针类型) const (指针变量) 时,指针的指向保持不变;
- 数组名是数组的首元素地址,是一个常量,不能够被修改;
- 指针+±-操作的是指针指向的类型宽度;
- 变量
- static局部变量作用域是在定义的函数内有效,其生命周期和程序的运行周期一致;
- 静态全局变量在函数外定义,作用范围被限制在所定义的文件中;
- 不同文件的static函数名可以相同,只能在定义这个函数的文件中使用;
- 打印–输出
- %p是打印地址的, %x是以十六进制形式打印;
//sizeof strlen
#include<stdio.h>
#include<string.h>
main()
{
char x[]="STRING";
x[0]=0; x[1]='\0';x[2]='0';
printf("%d %d\n",sizeof(x),strlen(x));
// 输出 7,0
}
参考:
-
黑马:C基础讲义.doc
-
与 或 异或
& 按位与, | 按位或 , ^ 按位异或
AND (位与&) OR ( 位或| ) XOR ( 位异或^ )
1 & 1 = 1, 1 | 1 = 1, 1 ^ 1 = 0
1 & 0 = 0, 1 | 0 = 1, 1 ^ 0 = 1
0 & 1 = 0, 0 | 1 = 1, 0 ^ 1 = 1
0 & 0 = 0, 0 | 0 = 0, 0 ^ 0 = 0
2. C++知识
-
命名空间std封装的是标准库的名称,标准程序库为了和以前的头文件区别,一般不加 .h;
-
C C++ 区别
- C语言中只有一个全局作用域;所有的全局标识符共享一个作用域;
- C++变量检测增强,不允许定义多个同名的全局变量;C++对于struct一组变量的集合,c++认为是一个新类型的定义说明;
- int fun( ); c语言中可以接受任意参数的函数;C++中表示无参函数;
- C++中新增bool类型;
-
C++ 中const修饰的,是一个真正的常量,而不是c中的只读变量,有自己的存储空间;
- C++中为全局变量且在其他文件中使用,或使用&取const常量地址时,会分配存储空间,const修饰引用时,也会分配存储空间;
-
引用:
- 引用在C++的内部实现是一个常量指针;Type& name 等价于Type* const name;
- 引用所占用的内存空间与指针保持一致;在64位系统中占用8个字节;
- 引用,指针,复制;
- 当函数的返回值为引用时,若返回栈区变量,不能成为其他引用的初始值,不能作为左值使用;若返回静态变量或全局变量,可以成为其他引用的初始值,既可以作为右值使用,可以作为左值使用;
- 被调用函数作为左值时,必须返回一个引用;const Type& name = var;让变量拥有只读属性;
-
函数的扩展:
- C++中的const常量替代宏常数定义;使用inline声明的内联函数替代宏代码片段;宏代码片段有预处理器处理,进行简单地文本替换,没有任何编译过程;
- inline只是一种请求,编译器不一定响应;内联函数省去了普通函数调用时的压栈、跳转和返回的开销;
- 默认参数和函数的占位参数结合可以为以后的程序扩展留下空间,兼容C程序;
- 函数重载:同一个函数名搭配不同的函数参数;参数个数/参数类型/参数顺序三者其一,注意重载中的默认参数,发生在一个类中;
-
struct定义类,所有成员的默认属性是public;class的默认属性是private;
-
C++ 编译器提供的默认copy构造函数、等号操作均属于浅拷贝;
-
类
- 成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关;
- 构造函数:当类中成员变量是其他类的对象时,首先调用成员变量的构造函数,调用顺序与声明顺序相同,之后调用自身类型的构造函数;析构函数(destructor)调用顺序与对应的构造函数相反;
-
对象的动态建立与释放:
- new delete : 用 new 分配数组空间时,不能够指定初始值;且应该判断返回值是否是空指针;
- Box *pt=new Box(12,15,18); new时可以直接对新建立的对象进行初始化;new申请堆空间未必成功,应判断返回值;
- malloc不会调用类的构造函数, Free不会调用类的析构函数;
- static静态成员变量由多个对象共享,局部于类,不是对象成员;
- 静态成员函数提供不依赖类数据结构的共同操作,没有this指针;调用:使用 类名:: 作限定词或通过对象调用;不能调用普通成员变量;
-
面向对象模型:
- 成员变量:普通成员变量 存储在对象中;静态成员变量存储在全局数据区中;
- 当类中含有static成员变量时,该变量是存储在静态区当中的,它是一个共享的量,因此,在类创建一个实例对象的时候,是无需再为static成员变量分配空间的,sizeof(对象)的大小排除静态成员变量;
- 成员函数存储在代码段;
- C++中的普通成员函数 都隐式包含一个指向当前对象的this指针;静态成员函数不包含指向具体对象的指针;
-
友元
- 若类B是类A的友元类,则B类中的所有成员函数均是类A的友元函数;
- 友元类通常设计为一种对数据操作或类之间传递消息的辅助类;
-
运算符重载
- 函数重载:一个函数名可以代表不同功能的函数;对一个已有函数赋予新的含义;
- 运算符重载的本质是一个函数;不能够进行重载的运算符: . :: .* ?: sizeof
- 可以对运算符做出新的解释;但不改变运算符的优先级、结合性、所需要的操作数,不创建新的运算符;
- 运算符重载只是定义了相对于一个特定类的新运算符,原有意义没有失去;
- 运算符重载的两种方法: //全局什么的看不懂;
- 运算符函数可以重载为 成员函数 或是 友元函数 ,区别在于成员函数有 this指针,而友元函数没有;但两者的使用方法相同;-- 但是传参的方法不同,实现代码不同,使用场合不同;
- 在参数需要隐式转换的情况下,使用友元函数 重载运算符是正确的的决定;
- 数组下标运算符[]
- 是 二元运算符;
- 只能用于 成员函数重载,不能用于友元函数重载;
- x[y] 可以理解为: x.operator
- x ( arg1, arg2, … ) 可以理解成: x . operator () (arg1, arg2, … )
- 以上两式 x 为对象;
看到5 没看完 P92
-
继承和派生
- C++的 继承方式(public、private、protected) 会影响子类的对外访问属性;
- 继承
- 子类 拥有父类的所有成员变量和成员函数;
- 子类可以拥有父类 没有的属性和方法;
- 子类对象 可以当做 父类对象使用;
- 派生
- 访问控制
- 派生类 继承了 除了构造和析构成员方法 之外的基类的全部成员函数及成员方法;
- 这些成员的访问属性,可在派生的过程中调整;
- 访问控制
- 继承方式改变继承成员的访问属性:
- public继承: 所有父类成员在子类中保持原有的 访问级别;
- private继承: 所有父类成员在子类中变成 private成员;
- protected继承: 父类中,public 变 protected;
protected 仍是 protected;
private 仍是 private; - private成员: 在子类中仍然存在,但却无法访问;
- 无论哪种方式继承 基类, 派生类都不能直接使用基类的私有成员;
- public、protected、private 的访问级别:
- public: 能够被外界访问的成员;
- protected: 当前类和子类中 被访问;
- private: 只能在当前类中被访问;
- 继承中的 构造和析构
- 公有派生类 具备了基类的所有功能,在需要基类对象的任何地方,都可以使用 公有派生类的对象进行替代;
- 子类就是特殊的父类;
- 继承中的对象模型
- 类 在C++编译器的内部可以理解为结构体;
- 子类 是由父类成员叠加子类新成员得到的;
- – 基类、派生类 – 父类、子类
- 继承中的构造 & 析构
- 子类对象构造时,需要调用 父类构造函数 对继承来的成员进行初始化;
- 子类对象析构时,需要调用 父类析构函数 清理继承来的成员;
- 继承中的 构造析构调用原则:
- 子类对象 在创建时会首先调用父类的构造函数;
- 当父类的构造函数执行结束后,执行子类的构造函数;
- 当父类的构造函数有参数时,需要在子类的初始化列表中 显式调用;
- 析构函数调用的先后顺序 与构造函数相反;
- 调用顺序: 继承与组合混合搭配的情况下:
- 先构造父类,再构造成员变量,最后构造自己的; //依次序多重继承时,先构造第一父类,依次继承构造;
- 先析构自己,再析构成员变量,最后析构父类; //与构造顺序 相反;
- 先构造的对象,后释放;
- 继承中同名成员变量 处理方法:
- 子类成员变量 与 父类成员变量 同名时,子类从父类中继承同名成员;
- 使用作用域 :: 进行同名成员的区分;-- 在派生类中使用基类的同名成员,要显式使用 类名限定符::
// d.base::b = 2; – d derived 派生类; - 同名成员存储在 内存的不同位置;
- 基类成员 的作用域延伸到所有派生类;
- 派生类的重名成员 屏蔽基类的同名成员; 屏蔽基类的同名成员函数,调用自身的成员函数;
- 派生类中的static 关键字:
- 基类定义的静态成员,将被所有派生类共享;
- 派生类中访问静态成员: 类名::成员 或是 对象名.成员
- static 访问
- 也遵守:3个访问原则: private protected public
- 派生类 对基类成员的访问 由继承方式和成员性质决定;
- 不但要初始化,更重要的 显式高速编译器分配内存;
- 构造函数默认private;
-
多继承
- 一个类 有多个直接基类的几个关系: --直接继承自多个基类;
- 虚继承:
- 一个派生类从多个基类派生,而这些基类又有共同的基类:则在对该类声明的名字进行访问时,可能产生二义性; 使用上层基类::x 访问上上层基类的成员; --P117
- 使公共基类在派生类中只产生一个子对象,必须将该基类声明为 虚继承; 使公共基类成为虚基类;
- 虚基类 声明使用关键字 virtual; ---- 再看 P119 图片分析;
-
多态
-
概述:
- 父类中被重写的函数依然会继承给子类; 默认情况下,子类中重写的函数将隐藏父类中的函数;
- 通过 作用域符号:: 可以访问父类中的被隐藏的函数; – 多态 看不懂了;
- 多态: 同样的调用语句 有不同的表现形态;
根据实际的对象类型决定函数调用语句的具体调用目标; - 父类和子类指针的步长不同,不要用父类指针++ 方式操作数组;
-
多态 实现
- C++通过virtual关键字对多态进行支持;
- 使用 virtual 声明的函数被重写后可以展现出多态特性;
-
多态成立的3个条件:
- 要有继承;
- 要有函数重写;
- 要有父类指针(父类引用)指向子类对象; --重要
– 根据指针所指向的实际对象类型 来判断如何调用;
// 多态时设计模式的基础,是框架的基础;
-
理论基础
- 静态联编 动态联编
- 联编: 一个程序模块、代码之间互相关联的过程;
- 静态联编: 程序的匹配、连接在编译阶段完成; --重载函数使用静态联编; – golang只有静态编译;
- 动态联编:程序联编推迟到运行时进行(迟绑定); --switch、if语句是动态联编;
- 静态联编 动态联编
-
函数重载 & 函数重写
- 函数重载
- 必须发生在同一个类中;
- 子类无法重载父类的函数,父类同名函数将被覆盖;
- 重载是在 编译期间 根据参数类型和个数决定函数调用;
- 函数重写
-
必须发生在 父类和子类 之间;
-
并且父类与子类中的函数必须具有完全相同的原型;
-
使用 virtual 声明之后能够产生多态,(如果不使用virtual,叫重定义)
-
= 多态是在运行期间根据 具体对象的类型决定函数调用;
一、重载(overload) (1)相同的范围(在同一个作用域中) ; (2)函数名字相同; (3)参数不同; (4)virtual 关键字可有可无。 (5)返回值可以不同; 二、覆盖(override)是指派生类函数覆盖基类函数,特征是: (1)不在同一个作用域(分别位于派生类与基类) ; (2)函数名字相同; (3)参数必须相同,即使参数协变也不行 (4)基类函数必须有 virtual 关键字。 (5)返回值相同(或是协变),否则报错; 有关 协变 的概念见 请见本人的另一篇文章http://blog.csdn.net/miyunhong/archive/2009/09/16/4557517.aspx 三、重写 (overwrite) (1)不在同一个作用域(分别位于派生类与基类) ; (2)函数名字相同; (3)返回值可以不同; (4)参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆) 。 (5)参数相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) 。 ==参考:== 1. []()
-
- 函数重载
-
派生类的 析构函数
- 析构 由基类指针建立的派生类对象 没有调用派生类的析构函数;
- 析构 由派生类指针建立的派生类对象 正确使用派生类析构函数;
-
-
多态原理探究
- 理论
- 虚函数表的指针 (vptr)
- 多态的实现原理
- 类中 声明虚函数时,编译器会在类中生成一个虚函数表;
- 虚函数表是一个存储类成员指针的数据结构;
- 虚函数 是有编译器自动生成与维护的;
- virtual成员函数会被编译器放入到虚函数表中;
- 存在虚函数时,每个对象都有一个指向虚函数表的指针; --vptr指针
- 编译器确定 函数是否是虚函数
- 不是虚函数,编译器可直接确定被调用的函数; --静态编译;
- 是虚函数,根据对象的vptr指针,在所指的虚函数表中查找fun()函数,并调用;
– 查找和调用 在运行时完成(实现所谓的动态编译)
与普通函数相比,虚函数的效率要低很多;
子类对象构造时,在父类的构造函数中调用虚函数,产生不了多态;
- 理论
-
纯虚函数和抽象类
- 基本概念
- 纯虚函数: 是一个在基类中说明的虚函数,在基类中没有定义,要求 任何派生类都定义自己的版本;
- 纯虚函数为各派生类提供了一个公共界面(接口的封装和设计、软件的模块功能划分);
- 说明式: virtual 类型 函数名(参数表) = 0;
- 一个具有纯虚函数的基类 称为抽象类;
- 抽象类
- 抽象类不能建立对象; ???
- 抽象类不能作为返回类型;
- 抽象类不能作为参数类型;
- 可以声明抽象类的引用; 可以声明抽象类的指针;
- 抽象类在多继承中的应用:
- 抽象类 可以模拟java的接口类;
- 多继承带来的代码复杂性远胜于其带来的便利;
- 在设计方法上,任何多继承都可以用单继承代替;
- C++中使用纯虚函数 实现接口;绝大多数面向对象的语言都不支持多继承;
- 多继承应用场景
- 接口类只有函数原型定义,没有任何数据的定义;
- 接口类 只是一个功能说明,而不是功能实现; 子类需要根据功能说明定义功能实现;
- 基本概念