编写、编译、连接、可执行代码。
声明变量包含:内存、内存单元名称。
函数包括两部分:函数原型,函数定义;
数据类型:基本类型(整型和浮点型)和复合类型(数组、字符串、枚举等)
声明并初始化的方法,=或{}或()。几种之间的区别,内存模型的区别。
定义常量:const
数组:double array[num]{};
定义指针:int * i;创建指针,计算机分配了用来存储地址的内存,但是不会分配用来存储指针指向的数据内存。
指针变量和指针常量:变量的地址就是指针常量,指针变量在声明的时候,指向地址的变量。
new分配内存:new开辟指定类型的内存,没有显式指定变量名称。
delete释放未命名的指针:删除的是指针指向的未命名内存,不能删除变量,不能重复删除,还可以重复给指针变量赋值。
数组的静态联编和动态联编。
动态联编数组:int *p = new int[10];删除数组delete [] p;使用数组p[0] = 1;
函数的三个位置:函数原型、函数定义、函数调用;
返回值类型不可以是数组,但是可以将数组作为结构或对象组成部分来返回。
为什么需要原型:允许将一个程序放在多个文件中,单独编译,然后组合;
形参:用于接收传递值的变量被称为形参,传递给函数的值被称为实参。
C++标准使用参数来表示实参,使用参量来表示形参。
自动变量:函数中声明的变量是函数私有的,在开始时分配,结束时销毁,这种变量为自动变量。
数组作为函数参数:int arr[]和int *arr相同。
指针作为参数的优点是节省复制整个数组所需的时间和内存,另外可能会增加破坏数据的风险。
const限定符:参数声明const int arr[],指针指向的是常量数据,
数组命名方式:修改型数组f_modify,不修改数组_f_no_change;
数组参数的两种形式:传入头指针以及数组长度,传入头指针和尾指针。
const用于指针两种方式:1、指向一个常量对象(禁止将const的地址赋值给非const指针);2、指针本身声明为常量int * const finger。
二级指针
指针参数尽可能使用const:如果条件允许,则应该将指针形参声明为指向const的指针。
结构参数传递:按值传参、引用传递、指针传递。
何时使用值传递结构:结构比较小时使用。
结构对象访问成员采用.;结构指针访问对象采用->;
类对象是基于结构的,因此可按值传参,指针传参,引用传参。
利用指针传参,优点避免了效率低下的问题,但代价是代码看起来更复杂;
函数指针:函数也有地址,函数的地址是存储其机器语言代码的内存的开始地址。可以编写将另一个函数的地址作为参数的函数。可以作为函数的参数。
函数作为参数传递:获取函数地址,声明函数指针,使用函数指针调用函数。
获取函数地址:使用函数名(后面不跟参数)即可。赋值时,两种的参数和返回值类型要相同。
声明函数指针:double (*pf)(int);其中*pf是函数,pf是函数指针。如果不加括号,则是返回指针的函数。
double pm(int); void estimate(int line,double (*pf)(int)); estimate(50,pm); double x=(*pf)(50);或pf(50) |
使用指针调用函数:(*pf)(5);也可以使用pf(5);
typedef创建类型别名:typedef double real;简化工具。
内联函数:在函数声明或定义前面添加inline,编译代码与其他程序代码内连起来,牺牲空间换时间。
引用变量:引用是已定义的变量的别名,主要用途是用作函数的参数(具体来说就是结构和对象参数),为处理大型结构提供一种非常方便的途径。
创建引用变量:int & rodents = rats;两者指向相同的值和内存单元。
使用引用变量的注意:必须在声明的时候进行初始化。
引用传参:使得函数中的变量名成为调用程序中的变量的别名。
常量引用:const int & ra,不允许在函数体内进行修改。
引用变量的适用性:基本类型建议采用值传参,当数据较大时,采用结构和类时,使用引用传参。
引用参数尽可能使用const:避免无意中修改数据的编程错误;能够处理const和非const实参,否则只能接受非const数据;能够正确生成并使用临时变量。
为何要使用返回引用:可以跳过临时变量的复制,效率更高。
返回引用的函数实际上是被引用的变量的别名。
返回引用时最重要的一点是,应避免返回函数终止时不再存在的内存单元引用。为了避免这种问题,最简单的方法是,返回一个作为参数传递给函数的引用,作为参数的引用将指向调用函数使用的数据;另一种方法是用new来分配新的存储空间,并返回指向该内存空间的指针(*p),这种方式的问题是不再需要时,需要用delete进行释放。
const int & test(int & p) { int p1; p1 = p; return p1;//违法使用。 } |
引用用于类对象:将类对象传递给函数时,C++通常的做法是使用引用。
引用与继承:基类引用可以指向派生类对象,而无需进行强制类型转换。(java中的变态)
何时使用引用参数:
使用的两个原因:
引用参数实际上是基于指针的代码的另一个接口。 对于不做修改的函数:
对于修改调用函数中数据的函数:
|
默认参数:1、函数参数右侧之后都为默认值;2、调用可以省略默认值;3、不可跳跃参数。
函数重载(多态):使用多个同名函数;
编译器在检查函数参数类型时,将类型引用和类型本身视为一个类型。
函数模板(泛型定义函数):template<typename AnyType>,其中template和typename是关键字,在定义函数的前面,typename可以替换成class。
单独编译:C++鼓励将组件函数放在独立的文件中,单独编译这些文件,然后将它们连接成可执行的程序。make
论述:如果将支持函数和main函数分开,两者文件都需要分别将共同的结构声明包含,直接包含,后期修改,需要对两个文件进行修改,无疑带来了麻烦,#include便可以处理这种情况,将声明放在头文件中,然后文件中使用#include进行包含,因此可以把程序分为三个部分。
程序的三个部分:头文件、源代码文件(结构有关的函数)、源代码文件(调用);
头文件:包含结构声明和使用这些结构的函数原型; 源代码文件:包含与结构有关的函数的代码; 源代码文件:包含调用与结构相关的函数的代码; |
如果编写一个程序时,使用这些函数,需要包含头文件,并将函数文件添加到项目列表或make列表中即可。
不要将函数定义和变量声明放到头文件中(内联函数可以)。头文件常包含的内容(函数原型、使用#define或const定义的符号常量、结构声明、类声明、模板声明、内联函数)
“”和<>两种包含头文件的方式:<>c++编译器将在存储标准头文件的主机系统的文件系统中查找;””编译器首先查找当前工作目录或源代码目录,如果没有则将在标准位置查找。
只需要将源代码文件加入到项目中,而不用加入头文件,这是因为#include指令管理头文件,另外不要使用#include来包含源代码文件,这样做将导致多重声明。
没有理解的内容:在IDE中,不要将头文件加入到项目列表中(这里的项目列表是不是指编译好的文件夹?)
#ifndef、#endif:仅当以前没有使用预处理器编译指令#define定义时,才处理#ifndef和#endif之间的语句;
#ifndef COORDIN_H_ #define MAX 4096 #endif |
多个库的链接:不同的编译器,会生成不同的名称修饰,链接编译模块时,因为名称的不同导致不能匹配的问题,在链接编译模块时,确保所有对象文件或库都是由一个编译器生成的。
存储数据的4种方案(区别在于数据保留在内存中的时间):1、自动存储持续性,在函数中定义声明的变量(包括函数参数)的存储持续性为自动的,在函数或代码开始时被创建,在执行完被释放,C++有两种自动变量。2、静态存储持续性,定义在函数外部和使用关键字static定义的变量,c++中有3中静态的冰凉。3、线程存储持续性,关键字thread_local声明的,声明周期和线程一样长。4、动态存储持续性,用new运算符创建,直到使用delete运算符将其释放或程序结束为止。
作用域:描述了名称在文件(翻译单元)的多大范围内可见。在文件中的函数定义之前定义的变量可在所有函数中使用。
连接性:描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享,自动变量没有链接性,不能共享。
C++作用域:作用域为局部的变量(定义它的代码块中可用);全局(也称为文件作用域,变量在定义位置到文件结尾之间都可用),函数原型作用域(包含参数列表的括号内),类中声明(作用域是整个类),在命名空间中声明的变量(作用域是整个命名空间,也称为全局,文件作用域是这里的一个子集)。
自动存储连续性:在函数中声明的函数参数和变量。
寄存器变量:关键字register修饰,建议编译器使用CPU寄存器来存储自动变量,旨在提高访问变量的速度。
静态持续变量:外部链接性(可在其他文件中访问),内部链接性(当前文件中访问),无连接性(当前函数或代码块中访问)。值不变,不需要栈管理。如果没有显式指定静态变量,默认为0.整个程序运行期间都存在。
外部链接性变量:在代码块外面声明它;内部链接性静态变量,代码块的外面声明它,并使用static限定符;没有链接性的静态持续变量,必须在代码块内声明,并使用static限定符。
单定义规则:1、定义,分配内存空间;2、引用声明,简称声明,不给变量分配存储空间,关键字extern,且不进行初始化。
多个文件中使用外部变量,只需要在一个文件中包含该变量的定义,其他文件中使用extern声明。
静态持续性、内部连接性(相对于静态持续性,外部链接性):static限定符用于作用域为整个文件的变量时,该变量的链接性是内部的,
静态持续性、无连接性:作用域在代码块中,但它在该代码不处于活动状态时,仍然存在,程序只在启动时进行一次初始化,以后调用不再初始化。
函数和链接性:默认情况下,函数的链接性为外部的,可以在文件间共享,如果只限定在一个文件中使用,利用static修饰原型和定义。
语言链接性,理解概念即可。
存储方案和动态分配:new分配的内存,称为动态内存,通常编译器使用三块独立的内存,一块用于静态变量,一块用于自动变量,另一块用于动态存储。
使用new分配内存并初始化:int * pi = new int(6);括号语法也可用于有合适构造函数的类。结构和数组初始化用大括号的列表,where *one = new where{2.5,33.2};int *ar = new int[4]{1,2,3,4},也可以将列表初始化用于单值变量int *i = new int{1};
命名空间是传统名称的作用区域的汇集与拓展。namespace name{}
命名空间可以是全局的,可以位于另一个命名空间中,但不能位于代码块中。默认情况下命名空间中声明的链接性为外部的(除非它引用了常量)
在一个命名空间提供了原型,可以在该文件或者另一个文件中再次使用相同的名称提供该函数的代码。也可以采用::访问命名空间。
using声明和using编译指令:using std::cout;using namespace std;
在头文件中声明namespace,在支持源文件中声明namespace,在调用文件中使用using。
类规范有两个部分组成:类声明,以数据成员的方式描述数据部分,以成员函数的方式描述公有接口;类方法定义,描述如何实现类成员函数。
C++将接口(类定义)放在头文件中,并将实现放在源代码文件中。
访问控制private(默认访问控制)、public、protected;
封装:实现细节封装,数据隐藏封装,类函数定义与实现放在不同文件中封装。
实现类成员函数:定义成员函数时,利用域解析符::来标识所属的类;类方法可以访问类的private组件。
非限定名:只能在类作用域中使用。
方法可以访问类的私有成员。
内联方法:定义位于类声明中的函数自动称为内联函数,在类声明之外定义,需要使用inline。
定义类:class name{private:member public:member}
创建对象:声明类变量;也可以使用new为类对象分配存储空间。
思想:公有部分的内容构成了设计的抽象部分(公有接口),将数据封装到私有部分中可以保护数据的完整性,这被称为数据隐藏,因此,C++通过类使得实现抽象、数据隐藏和封装等OOP特性。
声明和定义构造函数:类名(参数列表);没有void,没有显式返回值。
参数名不能与类成员相同,为避免,常见做法是在数据成员中使用m_前缀,或者在成员中时候_后缀。
使用构造函数:C++提供了两种使用构造函数来初始化对象的方式,其一显式调用Stock food = Stock(“World”,1);其二使用隐式调用构造函数Stock food(“Workd”,1);
使用new创建对象:Stock *food = new Stock(“World”,1);
默认构造函数:当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数,为类定义构造函数后,就必须为它提供默认构造函数。两种方式,1是提供为构造函数的参数提供默认值;2是使用重载定义一个无参的构造函数。注意Stock s;和Stock s();的区别。
析构函数:用构造函数创建对象后,程序负责跟踪该对象,直到过期为止,对象过期,自动调用一个特殊的成员函数,析构函数。~Stock(),没有参数。
类初始化的列表化语法:Stock tip = {“world”,1}或Stock tip{“world”,1}
const成员函数:void show() const;void stock::show() const;只要类方法不修改调用对象,就应该将其声明为const。
this指针:当方法涉及到两个类的对象,需要使用this指针。this指针指向用来调用成员函数的对象。*this为对象。每个成员函数都有一个this指针,指向调用的对象。
对象数组:声明对象数组方法与声明标准类型数组相同。Stock stuff[4];(默认构造函数)
声明对象数组:隐式初始化Stock stuff[4];显式初始化Stock stocks[10] = {Stock(“world”,1),Stock(“world”,2)}
对象数组必须要有默认构造函数:因为初始化过程,首先使用默认构造函数创建数组元素,然后使用花括号中的构造函数创建临时对象,最后将临时对象的内容复制到相应的元素中。
类作用域:类中定义的名称(数据成员名和类成员函数名)的作用域为整个类,类内是可知的,类外是未知的。
作用域为类的常量:使用枚举;使用关键字static;
运算符重载:例如operator +(),重载+运算符。
友元:友元函数、友元类、友元成员函数;
友元函数:非成员函数可以访问类的私有成员;
创建友元:friend time operator+(double m,const time & t);将其原型放在类声明中,并在原型声明前加上关键字friend,虽然在类声明中声明的,但他不是成员函数,因此不能使用成员运算符来调用;但它与成员函数的访问权限相同。
友元函数的定义:和普通函数一样,不需要类限定,不需要使用friend关键字。
类型转换:自动转换,强制类型转换,将类定义成与基本类型或另一个类相关,使得从一种类型转换为另一种类型具有意义。(其他内容,以后再看。)
Stonewt myCat; myCat = 19.6;//利用Stonewt(double) explicit 声明构造函数,可以关闭自动转换; 但是还可以使用强制类型转换(Stonewt) 19.6 |
静态类成员:static修饰,无论创建多少对象,程序只创建一个静态类变量副本,类的所有对象共享一个静态成员。不能在类声明中初始化静态成员变量,可在类声明之外使用单独的语句来进行初始化,初始化语句指出了类型,并且使用了作用域运算符,但没有使用关键字static。(较多的是在方法文件中初始化)
对于不能在类声明中初始化静态数据成员的一种例外情况是,静态数据成员为const整数型或枚举型;
动态内存:例子,构造函数中定义一个指针,初始化时,将指针对象new,然后传递给对象定义的指针,并且在析构函数使用delete,进行删除指针。
const返回值:这种修饰返回值是引用类型的情况下,为了避免返回值被修改的情况,返回值是引用的函数,可以肯定的是这个引用必然不是临时对象的引用,因此一定是成员变量或者是函数参数,所以在返回的时候为了避免其成为左值被修改,就需要加上const关键字来修饰。
const修饰返回值,const修饰参数,const修饰成员函数。
特殊的成员函数:默认构造函数、默认析构函数、复制构造函数、赋值构造函数、地址运算符。
复制构造函数:class_name(const Class_name &);
何时调用复制构造函数:
StringBad ditto(motto); StringBad metoo = motto; StringBad also = StringBad(also); StringBad * p = new StringBad(motto); 当程序生成了对象副本时,编译器都将使用复制构造函数,具体来说,当函数按值传递对象或函数返回对象时,都将使用复制构造函数。 |
默认的复制构造函数的功能:逐个复制非静态成员(浅复制),复制的是成员的值,如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员对象。(当涉及到对象统计静态变量和指针创建时,需要考虑重写复制构造函数。)深复制。
在构造函数中使用new时注意的事项:1、如果在构造函数中使用new,在析构函数中就使用delete;2、如果有多个构造函数,必须以相同的方式使用new。3、new和delete要匹配,new[]要使用delete[];4、应定义一个复制构造函数,通过深度复制将一个对象初始化另一个对象;5、应定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象。
有关返回对象的说明:1、返回对象的引用,返回其中一个引用形参,旨在提高效率,通常用const修饰,避免被赋值;2、返回指向非const对象的引用:常见于连续赋值,以及从在<<运算符等情况;3、返回对象:重载的算术运算符属于这一类(返回一个新的对象,为两个参数的和等。);4、返回const对象:防止被当做左值赋值;
使用指向对象的指针:class * p = new class();访问p->fun();*p接触指针。
继承:可以通过复制原始类代码,并对其进行修改来完成开发工作,但继承机制只需要提供新特性,甚至不用访问源代码就可以派生出类。
派生类:class Rate:public TableTennis{}
公有继承:基类的公有成员称为派生类的公有成员,私有成员称为派生类的一部分,但只能通过基类的公有和保护方法访问。
派生类:1、存储了基类的数据成员;2、派生类对象可以使用基类的方法(继承了基类的接口);
派生类的构造函数:派生类不能访问基类的私有成员,而必须通过基类方法进行访问,所以派生类构造函数必须使用基类构造函数,
成员初始化列表语法:
Rate::Rate(int r ,const string & fn,const string & ln,bool ht):TableTennis(fn,ln,ht){rating = r}; 如果省略成员初始化列表,必须首先创建基类对象,如果不调用,则提供默认的基类构造函数。 派生类成员也可以使用成员初始化列表语法:Rate::Rate(int r,const TableTennis & tp):TableTennis(tp),rating(r);其中rating是派生类成员名 |
派生类构造函数的要点:1、首先创建基类对象;2、派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;3、派生类构造函数应初始化派生类新增的数据成员。
派生类一般和基类声明放在一起,也可以分开放。
派生类和基类的特殊关系:1、可以调用基类的非私有方法;2、基类指针可以在不进行显示类型转换的情况下指向派生类对象;3、基类引用可以不在显式类型转换的情况下引用派生类。4、基类指针或引用只能调用基类方法;(virtual是解决重写方法调用的问题。)
C++代码的关系思想:is-a(apple is a kind of fruit);has-a(午饭有苹果);is-like-a(律师像鲨鱼);is-implatemented-as-a以及uses-a
多态(多态公有继承):同一个方法在派生类和基类中的行为不同;两种方式实现多态公有继承:1、在派生类中重新定义基类的方法;2、使用虚方法。
如果方法是通过引用或指针而不是对象调用的,如果没有使用关键字virtual,程序根据引用类型或指针类型选择方法。如果使用virtual,程序将根据引用或指针指向的对象的类型来选择方法。
如果要在派生类中重新定义基类的方法,通常应该将基类方法声明为虚的,这样程序根据对象类型,而不是引用或指针的类型来选择方法版本,为基类声明一个虚析构函数也是一种惯例。
非构造函数不能使用成员初始化列表语法,但派生方法可以调用公有的基类方法:Brass::viewAcct();必须使用作用域解析运算符,没有重新定义的方法,直接继承过来,不需要作用域运算符。
静态联编:将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编,在编译过程中联编称为静态联编,虚函数由于根据对象指针或引用才能确定调用函数,所以称为动态联编。C++动态联编通过指针和引用调用方法相关。
将派生类引用或指针转换为基类引用或指针被称为向上强制转换,公有继承不需要进行显式类型转换。相反,是不允许隐式转换的,
使用虚函数在内存和速度方面有一定的成本。
析构函数应当是虚函数,除非类不用做基类。友元不能是虚的,因为不是成员;
protected:类外相当于private,类内相当于public;
抽象基类:拥有纯虚函数即函数声明加上=0;允许纯虚函数有定义。
is-a一般设计为公有继承,has-a有两种方式实现,一种是包含,一种是私有继承。
多重继承的问题:1、同名方法问题(从新定义方法,或者使用对象.继承类::方法()进行调用。);2、同一个类多个实例(虚基类);
友元类friend class remote;
异常:try...catch;
智能指针:
文件读取:
泛型及类模板:
数据库连接:
线程和进程及线程池技术;
网络通信:
动态链接和静态链接