C++总结

C++学习总结(清华大学教材)
第四章
4.1内联成员函数
使用内联函数可以减小开销。声明有两种方式:隐式和显式。
将函数体放在类中—隐式;用online关键字—显式。
4.2构造函数和复制构造函数
既可以在定义的时候实现,也可以在在类的定义外实现。均可以使用:来给私有数据成员赋值。
eg: class Point{
public:
Point(int a, int b);
Point(Point&p);
private:
int x; int y;int count;
};
Point::Point(int a, int b)
{
x = a;y = b;count++;
}
Point::Point(Point&p)
{
x = p.x; y = p.y; count++;
}
}
///等价于如下所示/
class Point{
public:
Point(int a, int b):x(a),y(b){count++;}
Point(Point&p):x(p.x),y(p.y){count++;}
private:
int x;int y;int count;
};
}
4.3类的组合
类的组合描述的就是一个类内嵌着其他类的对象作为成员,他们之间有着包含的关系。
组合类构造函数的形式:
类名::类名(参数表):内嵌对象1(形参表)…内嵌对象n(形参表)(类的初始化)
调用内嵌对象的构造函数,顺序为在组合类中定义的顺序进行调用。

第五章
5.1.标示符的作用域和可见性
当在两个或多个具有包含关系的作用域中声明了同名的标识符,则外层标示符在内层不可见。
5.2.静态生存期和动态生存期
5.3.类的静态成员:静态函数成员和静态数据成员
静态数据成员–声明static int count ;(类中); 定义和初始化 int Point::count=0;(类外)可以实现同一类的数据共享。对于静态数据成员的访问用类名限定,而不是用对象名,主函数里面不允许直接访问。
静态函数成员–static void f();
静态函数成员通过类名或对象名来调用。
5.4.友元函数和友元类
友元关系提供了不同类或对象的成员函数之间,类的成员函数与一般函数进行数据共享的机制。使用friend关键字声明。
友元函数:eg: friend float dist(Point &p1, Point &p2);//形参是引用
在main里面float dist(Point &p1, Point &p2){……}//这个友元函数可以访问到类中的私有成员
友元类:eg:friend class A;
注意:友元关系不能传递;友元关系是单向的;友元关系不能被继承。
5.5.常对象:const A a(3);//初始化常对象
常成员函数:声明void print() const; 定义void A::print() const{…} 常对象只可以调用它的常函数。
常数据成员(常数据成员只能通过初始化类表来获得初值—构造函数):const int a;
声明static const int count; 定义和初始化 const int A::count=0;
常引用:常引用所引用的对象不能被更新。
5.6.多文件结构和编译预处理命令
自己可以将类的定义,实现和使用分开写成三个文件,定义写作头文件.h,使用#include”.h”来调用,实现写作.cpp文件(里面可以写构造函数和复制构造函数的实现以及一些静态变量的赋值),然后实现一定在main函数里面,所以主函数.cpp文件包含.h头文件,可以实现分层封装等操作。
注意:一般将分配空间的定义放在源文件,而将不需要分配空间的声明放在头文件里面。内联函数的定义需要放在头文件里。
5.7. 外部变量和外部函数
外部变量的声明分为定义性声明和引用性声明。定义性声明:在声明时同时定义。引用性声明:引用别处定义的变量。
调用外部函数的时候进行引用性声明就可以了。也可以在声明函数原型的时候或者定义函数的时候用extern 修饰。
注意:通常时候,变量和函数的定义都放在源文件中,而对外部变量和函数的引用性声明放在头文件中。

5.8. C++中最好使用类中用const代替C语言中的#define语句。

第六章
6.1.数组
数组在内存中的存储是顺序,连续存储的。如果定义数组的时候没有指定任意一个元素的初值,对于静态生存期的数组来说,每个元素初值为0.对于动态生存期的数组来说每个元素的初值是不确定的。数组的初始化:int a[2][3]={1,2,1,2,1,2};或者int a[2][3]={{1,1,1},{2,2,2}};
6.2.数组作为函数参数
数组元素和数组名都可以作为函数的参数来实现函数间的数据的传递和共享。
注意:数组名代表第一个元素的地址,做传参的时候,形参必须是地址或者数组的名字。且形参数组元素进行改变的时候,实参数组元素也会变化。实参数组的元素个数不应少于形参数组元素的个数。把数组作为参数的时候一般不指定第一维的大小。
6.3.对象数组
定义对象数组:类名 数组名[常量表达式]; 数组元素每一个都是一个对象。
通过数组里的对象访问到公有成员:数组名[下标表达式].成员名;
初始化对象数组就是调用类定义的构造函数初始化对象,如果没有指定数组元素的初始值,就调用默认构造函数。
Class Point{ void fff();};-- Point a[2]; a[1].fff();
6.4. 指针
地址编码的基本单位是字节。
指向常量的指针,不能通过指针来改变常量的值,但是指针本身可以改变,即可以指向别的对象。Int a = 10 ; const int *p = &i; int b = 1; p = &b; 这是可以的。
指针类型的常量,这时指针本身不可以被改变。Int const *p2 = &a; p2 = &b;这是不可以的。
不能声明void类型的变量,但是可以声明void类型的指针。
指针的算术运算:

指针的关系运算:0通常代表空指针,也就是一个不指向任何有效地址的指针。同类型的指针才可以进行关系运算,与0也可以。
注意:把数组作为形参,等价于把指向数组元素类型的指针作为形参。
eg: void f(int a[]); == void f(int a[3]); == void f(int *p);
6.5.指针数组
一个数组的每一个元素都是同类型的指针。数据类型 *数组名[下标表达式];
Eg: int *p[3];由于指针数组的每一个元素都是指针,必须先赋值再引用。
***即使用指针数组的时候,每一元素都是一个指针(地址)。
用指针做参数的时候,由于与实参指向同一个地址,所以形参改变,会导致实参也改变(同引用作用一样。)

数组指针
6.6. 指针型函数
当一个函数的返回值类型为指针时,这个函数就是指针型函数。形式:
数据类型 *函数名(参数表){函数体}
int *fff(int i){…}
6.7. 指向函数的指针:函数名代表函数起始地址。
形式:数据类型 (*函数指针名)(形参表)
先定义实现函数void fff();void (*function)(float); function = fff; 这时function这个函数指针就指向了fff函数。Function(3.2)就等价于fff(3.2);实现调用函数。
6.8.对象指针
对象指针就是用于存放对象地址的变量。形式:类名 *对象指针名;
Eg : Point *p1; Point p2; p1 = &p2;使得p1指向p2。
使用对象指针访问对象的成员:对象指针->成员。等价于(*对象指针名).成员名。
注意:可以通过对象名也可以通过对象地址来访问一个对象,对象占据的内存空间只是用于存放数据成员,函数成员不在每一个对象的存储副本中。

6.9 .指向类的非静态成员(对象的成员)的指针
指向对象成员的指针使用前要先声明,再赋值,然后再引用。声明指针语句如下所示:
类型说明符 类名::*指针名; class Point; int Pint::*p; //声明指向数据成员的指针
类型说明符 (类名::*指针名)(参数表); int (Point::*qq)(…);//声明指向函数成员的指针
声明之后要对其进行赋值:
对数据成员指针的赋值一般为: 指针名 = &类名::数据成员名; p=&Point::count;
在进行访问的时候使用: 对象名.*类成员指针名 或者对象指针名->*类成员名
对函数成员指针的赋值一般为:指针名 = &类名::函数成员名; qq=&Point::fff;
在进行访问的时候使用:
(对象名.*类成员指针名)(参数表)或者(对象指针名->*类成员指针名)(参数表)
6.10 .指向类的静态成员(对象的成员)的指针
由于类中的静态成员的访问可以不依赖于对象,所以可以用普通的指针来访问。
6.11. 动态内存分配
在程序运行过程中申请和释放的存储单元称为堆对象。建立和删除的运算符:new 和 delete.

建立的语法为: new 数据类型 (初始化参数列表)建立成功返回指向申请的内存单元地址的指针。Eg: int *p; p = new int(2);//分配了存放int类型数据的空间,并将2存进去。如果不写括号,则不写入初值;如果括号里面为空,则写入0;如果建立对象是某个类的实例,则根据结构调用构造函数。注意:建立时new Point调用默认构造函数;new Point()调用默认构造函数,还将赋值成员数据和指针为0。Point *ptr = new Point();
删除的语法为: delete 指针名; 如果删除的是对象,则调用析构函数。delete ptr;
注意:用new建立的对象,必须用delete删除,否则会导致内存无法回收,出现内存泄漏现象。

用new建立数组类型的对象。new 类名[数组长度];若后面加上了括号则为数组赋初值0。Int *p = new array2; 删除数组对象用delete[] 指针名;delete[] p;
6.12. vector创建数组对象
Vector是一种类的模板。使用形式: vector <元素类型>数组对象名(数组长度)
可以初始化vector <元素类型>数组对象名(数组长度,元素初值) vector arr(2,0);
但是vector数组对象的名字就是一个数组对象,不是首地址,因为其实是一个封装了数组的对象,不是数组。Vector定义的数组对象重要的成员函数size(),返回数组的大小。

6.13.深复制和浅复制。

6.14.字符串
在C中使用字符数组来存放字符串,在C++中沿用了这种方法还预定义了string类。
沿用C中的方法:字符串在内存中按照顺序存放,字符串常量表示这样一个数组的首地址。因为是常量,所以要定义一个指向常量的指针。
eg: const char *point = “china”; cout<<point<<endl;
字符串变量也类似,eg: char str[8] = {‘c’,’h’,’I’,’n’,’a’,’\0’};char str[8] = “china”;
使用string类:

一般cin输入字符串都是以空格作为分隔符
getline的用法:getline(cin , s2)//输入字符串,且不以空格作为分隔符。
getline(cin , s2, ‘,’);// 输入字符串,且以逗号作为分隔符。

第七章
7.1.继承和派生
继承的方式限定了如何访问从基类继承的成员。继承方式的关键字:private,protected,public。而派生类的成员指的是从基类继承来的所有成员外的,新增加的数据和函数成员。
形式:class 派生类名:继承方式 基类名1,继承方式 基类名2…….,继承方式 基类名n
吸收基类成员—改造基类成员—添加新的成员。
注意:吸收的是基类中除了构造函数和析构函数外的所有非静态成员。如果派生类声明了一个和某基类成员同名的新成员(如果是函数,则要求参数表也要相同,否则就是重载),派生的新成员就隐藏了外层同名的成员。(同名隐藏)
7.2.访问控制
公有继承:基类的公有成员和保护成员的访问属性在派生类中不变(派生类的其他成员和其对象都可以直接访问到),而基类的私有成员不可以直接访问。
私有继承:基类中的公有成员和保护成员都以私有成员身份出现在派生类中(基类的公有成员和保护成员可以被派生类中其他成员访问,但是类外不能通过派生类的对象直接访问到),而基类的私有成员在派生类中不可以直接访问。(基类的私有成员,无论是派生类的成员,还是派生类的对象都无法直接访问)
保护继承:基类的公有成员和保护成员都以保护成员出现在派生类中,基类的私有成员不可以直接访问到。(派生类的其他成员可以访问到基类的公有和保护成员,但是类外对象不可以访问到。)
7.3.类型兼容规则
指在需要基类对象的时候,可以使用公有派生类的对象替代。(但只能使用继承过来的成员)
使用情况:派生类的对象可以隐含转换为基类的对象—B b;D d;b =d;
派生类的对象可以初始化基类的引–B &b1=d;
派生类的指针可以隐含转换为基类的指针—B *p = &d;(P是对象指针)
根据规则:在基类对象出现的场合使用派生类对象进行替代,但是替代后仅仅发挥出基类的作用。
7.4.派生类的构造和析构函数
构造派生类对象时,就要对基类的成员对象和新增成员对象进行初始化。
一般形式如下
派生类名:派生类名(参数表):基类名1(基类1初始化参数表),……,基类名n(基类n初始化参数表),成员对象1(成员对象1初始化参数表),……,成员对象n(成员对象n初始化参数表){ 派生类构造函数的其他初始化操作; }
如果对基类初始化时,需要调用基类的带有形参表的构造函数时,就必须声明构造函数。
构造函数的执行顺序为:调用基类构造函数的顺序按照他们被继承时声明的顺序;对派生类新增成员对象的初始化按照他们在类中的声明顺序;最后执行构造函数括号里的内容。

7.5派生类成员的标识和访问
成员按访问属性分为以下四种;
不可访问成员:从基类继承过来,派生类或对象都无法访问,继续派生也是无法访问。
私有成员:包括从基类继承过来以及新增加的成员,在类中可以访问,但是建立对象无法访问,派生出新类无法访问。
保护成员:从基类继承过来以或者新增加的成员,派生类内部可以访问,建立对象无法访问,进一步派生可能成为私有成员或者保护成员。
公有成员:派生类,建立的对象都可以访问到,继续派生,可能是新派生类中的私有、公有和保护成员。
7.6作用域分辨符(::)
(1)基类与派生类有同名的函数的时候,即使函数参数表不一样,也不会算作函数重载,而是派生类会隐藏掉基类的函数。这是对象名.函数就只能访问到派生类,想要访问基类需要作用域分辨符::。
eg: class base1;class base2;class derived : public base1,public base2{}; —derived dd; dd.fun(); dd.base1::fun();dd.base2::fun();-----解决了基类成员被隐藏的问题。
使用using关键字来澄清:在派生类中使用using base1::fun();来澄清,这样就不会被掩盖,即使using base1::fun();void fun(int i);这样也是两个重载函数并存在派生类中。
(2)派生类中继承的基类们有部分或全部可能来自同一个基类,这样基类继承出来的成员就会有同名的成员,派生类中就必然会产生同名成员,这样必须使用作用域分辨符来唯一标识。
7.7虚基类
出现派生类中继承的基类们有部分或全部可能来自同一个基类,这样基类继承出来的成员就会有同名的成员,派生类中就必然会产生同名成员,这样可以使用作用域分辨符来唯一标识,也可以将共同基类设置为虚基类。
虚基类的声明:class 派生类名:virtual 继承方式 基类名
虚基类的成员再进一步派生的时候共同维护一个内存数据副本。这样建立派生类对象时,可以直接访问成员。这样相比下,前者方法可以存放更多数据,后者更加简单,节省内存空间。
虚基类的构造函数有参数时,所有直接和间接的派生类都要在构造函数里面初始化虚基类。

第八章
8.1 多态性
多态是指同样的消息被不同类型的对象接收时导致不同的行为。多态分为重载、强制多态和包含、参数多态。前两种为专用多态,后两种为通用多态。
8.2多态的实现
编译时的多态和运行时的多态。静态绑定可以实现重载、强制和参数多态,动态绑定可以实现包含多态。
8.3运算符重载(重载多态)
运算符重载是指同一个运算符在不同的数据类型时导致不同的行为。
重载规则:1.只能重载C++中有的运算符2.重载后优先级个结合性不变3.重载后功能类似。
重载分为重载为类的非静态成员函数和重载为非类成员函数。一般形式为:
返回类型 operator运算符(形参表){函数体}
其中operator是定义运算符重载的关键字,当重载为类的非静态成员函数的时候,函数的参数个数比原来的操作数要少一个(后置++ – 除外),重载为非类成员函数时,参数个数与原来操作数个数相同。

重载为成员函数的情况:1.双目运算符x。对于Point1 x Point2,可以将x重载为Point1类的成员函数,这样形参就是Point2所属的类型,调用成员函数Point1.operatorx(Point2) 就相当于是Point1 x Point2。2.前置单目运算符y。对于y Point1, 可以将y重载为Point1类的成员函数,这样形参就是空的,调用成员函数Point1.operatory() 就相当于是 x Point1。3.后置单目运算符z。对于Point1 z, 可以将z重载为Point1类的成员函数,这样形参要带一个整形int,调用成员函数Point1.operatorz(0) 就相当于是 Point1 z,这里0不起任何作用只是用来区分后置的+±-和前置的+±-。
eg:双目运算符+和-的部分程序

也可以使用如下程序段代替:
complex complex ::operator+(const complex &r1)const
{
complex c(real+r1.real,image+r1.image);
return c;
}
重载为非成员函数的情况:1.双目运算符x。对于Point1 x Point2,只要有一个具有自定义类型就可以将x重载为非成员函数,这样形参就是Point1和Point2,表达式operatorx(Point1,Point2) 就相当于是Point1 x Point2。2.前置单目运算符y。对于y Point1, 其中Point1具有自定义类型,可以将y重载为非成员函数,这样形参就是Point1,这样调用operatory(Point1) 函数就相当于是 x Point1。3.后置单目运算符z。对于Point1 z,其中Point1具有自定义类型,可以将z重载为非成员函数,这样形参有两个,一个是Point1一个是int,operatorz(Point1,0) 就相当于是 Point1 z,这里0不起任何作用只是用来区分后置的+±-和前置的+±-。
8.4虚函数
虚函数是动态绑定的基础,虚函数必须是非静态成员函数。虚函数经过派生后,在类族中可以实现运行过程中的多态,基类指针指向派生类对象的时候,就可以访问派生类成员了。
一般虚函数成员声明语句:virtual 函数类型 函数名(形参表)
注意:虚函数的声明只能在类定义中的函数原型中声明,而不能在成员函数实现的时候。运行过程中的多态需要满足三个条件:1.满足赋值兼容规则2.要声明虚函数3.由成员函数来调用或者通过指针、引用来访问虚函数。
8.5虚析构函数
在C++中不能声明虚构造函数,但是可以声明虚析构函数 语法:virtual ~类名();
可以保证使用基类类型的指针就能够调用适当的析构函数针对不同的对象进行清理工作。
如果不声明是虚函数,则基类指针指向派生类对象也只会调用基类的函数。
8.6抽象类与虚函数
抽象类为了抽象和设计而建立的,它不能实例化,即无法定义一个抽象类的对象,只能通过继承机制,生成抽象类的非抽象派生类,然后再实例化。抽象类带有纯虚数类。
纯虚函数:在基类声明的纯虚函数,在基类中没有定义具体的实际操作,要求各派生类根据实际需要给出各自的定义。声明格式:virtual 函数类型 函数名(参数表)=0;声明为纯虚函数之后,在基类中就可以不给出函数实现。注意:如果在基类中调用纯虚函数,必须加上类名来限定。
抽象类就是带有纯虚函数的类。抽象类声明了一个类族派生类的共同接口,派生类如果全部实现了虚函数,那么就可以实例化,否则还是一个抽象类。注意:抽象类不能实例化,但是可以定义一个抽象类的指针和引用,通过这种方式来访问派生类的对象,进而访问派生类的成员,这就具有多态特征。
8.7多态类型和非多态类型
多态类型是指有虚函数的类类型,非多态类型指其他所有类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值