C++学习回顾(2)

C++学习回顾(2)

对本文C++学习时经常学完就忘的知识点进行记录(第二部分)
本文主要参考了郑莉著的《C++语言程序设计(第四版)》

6 数组、指针与字符串

数组
数组的初始化就是在声明数组时给部分或全部元素赋初值。
数组也可以声明为常量,但是它们的值在初始化后皆不可以改变。

const float fa[5] = {1.02.03.0}

数组作为函数参数
数组元素和数组名都可以作为函数的参数实现函数见数据的传递和共享。
使用数组名传递数据时,传递的是地址。
注意:在子函数对数组形参元素的操作结果直接影响到函数实参的相应元素,因为传递的是地址。
指针
指针是专门用来存放内存单元地址的变量类型。
直接的地址操作手段,相比值得传递,地址传递可以减少系统开销,提高效率。
指针也是一种数据类型,具有指针类型的变量称为指针变量,指针变量是用来存放内存单元地址的。
通过指针访问变量的方式是间接的。

//指针的声明
数据类型 * 标识符;
int * ptr;

*表示声明的是一个指针类型的变量。
指针的类型——表面指针所指的内存单元用于存放什么类型的数据。
指针可以指向各种类型,包括基本类型、数组、函数、对象,同样也可以指向指针。
**运算符 * 和 & **
*表示指针运算符,也称解析,表示获取指针所指向的变量的值,例如 ptr 表示的是指针ptr所指向的int类型的数据的值。
& 称为取地址运算符,用来得到一个对象的地址。例如 &i 就可以得到变量i的存储单元地址。
注意:"
"和 "&"出现在声明语句和执行语句中的含义是不同的。
&在声明中表示引用,*在声明中表示指针变量。
指针的赋值

  • 在定义指针的同时进行初始化赋值。
存储类型 数据类型 * 指针名 = 初始地址;
  • 在定义之后使用赋值语句赋值
指针名 = 地址;

指向常量的指针和指针类型的常量

  • 声明指向常量的指针,指针指向的是常量,因此不能通过指针改变所指对象的值,但指针本身可以改变,可以指向另外的常量。
int a;
const int * ptr1 = &a;  //ptr1是指向常量的指针
int b;
ptr1 = &b; //正确,可以指向另外的常量
ptr1 = 1; //错误,不可以通过指针改变所指对象的值
  • 声明指针类型的常量,指针本身的值不能被改变。
int a;
int * const ptr2 = &a; //指针类型的常量,ptr2只能指向a是地址,不能再被改变指向其他地址
int b;
ptr = &b; //错误

指针运算
指针的加减运算结果与指针的类型密切相关。
p1+n1表示指针p1当前所指位置后方第n1个数的地址。
*(p1+n1)表示指针p1当前所指位置后方第n1个数的内容,也可以写作p1[n1]。
指向相同类型数据的指针可以进行关系运算,如果两个相同类型的指针相等,则表示这两个指针指向同一个地址。
0专用于表示空指针,也就是一个不指向任何有效地址的指针。

int *p;
p = 0; //空指针
p = NULL; //与上面表示等效。

用指针处理数组单元
数组名表示数组的首地址。

array 和 &array[0] 相同
*array等同于array[0],*(array+3)等同于array[3]

指针数组
如果一个数组的每个元素都是指针变量,这个数组就是指针数组,并且指针数组的每个元素都必须是通过一个类型的指针。

数据类型 * 数组名[下标表达式]int * pa[3]; //声明的int类型的指针数组pa,有3个元素,每个元素都是指向int类型数据的指针。

用指针作为函数参数
以指针作为函数形参,在调用时实参将值传递给形参,也就是使实参和形参指针变量指向同一个内存地址。因此,子函数运行过程中,通过形参指针对数据值的改变也同样会影响着实参指针所指向的数据值。
当某个函数中以指针或引用作为形参都可以达到相同目的,使用引用会使程序的可读性更好些。
指针型函数
当一个函数的返回值是指针类型时,这个函数就是指针型函数。
使用指针型函数的最主要目的就是要在函数结束时把大量的数据从被调函数返回到主调函数中。

//指针型函数的一般定义形式
数据类型 * 函数名(参数表)
{
	函数体
}

指向函数的指针
每一个函数都有一个函数名,实际上这个函数名就表示函数的代码在内存中的起始地址。
函数指针就是专门用来存放函数代码首地址的变量。

数据类型 (* 函数指针名)(形参表)
函数指针名 = 函数名;

用函数指针调用函数时需要加上形参表。
对象指针
对象指针就是用于存放对象地址的变量。

类名 * 对象指针名;
对象指针名->成员名

对象指针在使用前一定要先进行初始化,让它指向一个已经声明过的对象。
this指针
this指针是一个隐含于每一个类的非静态成员函数中的特殊指针(包括构造函数和析构函数),它用于指向正在被成员函数操作的对象。
每次对成员函数的调用都存在一个目的对象,this指针就是指向这个目的对象的指针。
指向类的非静态成员的指针
类的成员自身也是一些变量、函数或者对象等,因此也可以直接将它们的地址存放到一个指针变量中,这样,就可以使用指针直接指向对象的成员,进而可以通过这些指针访问对象的成员。
声明指向对象成员的指针:

类型说明符  类名::*指针名;  				//声明指向数据成员的指针
类型说明符  (类名::*指针名) (参数表);  	//声明指向函数成员的指针

//对数据成员指针赋值
指针名 = &类名::数据成员名;

//访问数据成员
对像名.*类成员指针名
//或
对像名->*类成员指针名

//访问函数成员
int main(){
	Point a(4,5);
	Point *p1 = &a;
	int (Point:: *funcPtr) () const = &Point::getX;
	cout<< (a.*funcPtr) ()<<endl;
	cout<< (p1->*funcPtr) ()<<endl;
}

指向类的静态成员的指针
对类的静态成员的访问不依赖于对象,因此可以用普通的指针来指向和访问静态成员。

int main(){
	int *ptr = &Point::count;  //指向类的静态成员
	Point a(4,5);
	cout<< "Object count"<<*ptr<<endl;   //直接通过指针访问静态数据成员
}

动态内存分配
在程序运行过程中申请和释放的存储单元称为堆对象,申请和释放过程一般称为建立和删除,new和delete。
深复制与浅复制
隐含的复制构造函数是浅复制。
在这里插入图片描述
在这里插入图片描述
浅复制的弊端:程序结束之前析构函数可能被多次调用,同意内存空间可能会被多次释放。浅复制只是两个指针指向相同的内存地址,并没有形成真正的副本。
字符串
C++中预定义了“string”类,每个字符占一个字节,并在末尾添加‘/0’作为结尾标记。实际上是一个隐含创建的类型为char的数组,一个字符串常量就表示这样一个数组的首地址
在这里插入图片描述
string类常用成员函数功能简介
在这里插入图片描述

getline(cin,s2);   //以换行符作为分隔符
getline(cin,s2,',');  //以逗号作为分隔符

继承与派生

类的继承,是新的类从已有的类那里得到已有的特性。从另一个角度看,从已有类产生新类的过程就是类的派生。
派生类的定义

class Derived:public Base1,public Base2      //多继承
{
public:
	Derived();
	~Derived();
}

一个派生类同时有多个基类的情况称为多继承,一个派生类只有一个直接基类的情况称为单继承。
直接参与派生出某类的基类称为直接基类,基类的基类甚至更高层的基类称为间接基类。
在派生类的定义中,继承方式规定了如何访问从基类继承的成员。
派生类的生成过程
构造函数和析构函数不被继承。派生类继承了基类中除了构造函数和析构函数之外的所有非静态成员。
对基类数据成员隐藏:如果派生类中声明了一个与某基类成员同名的新成员(如果是成员函数,则参数表也要相同,参数不同的情况属于重载),派生类的新成员就隐藏了外层同名成员。
继承方式与访问控制
从基类继承的成员,其访问属性由继承方式控制。
当类的继承方式为公有继承时,基类中的公有成员和私有成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。
当类的继承方式为私有继承时,基类中的公有成员和私有成员都以私有成员的身份出现在派生类中,而基类中的私有成员在派生类中不可直接访问。
当类的继承方式为保护继承时,基类中的公有成员和私有成员都以保护成员的身份出现在派生类中,而基类中的私有成员在派生类中不可直接访问。
类型兼容规则
类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象替代。
类型兼容规则所指的替代包含以下几种情况

  • 派生类的对象可以隐含的转换为基类对象。
  • 派生类的对象可以初始化基类的引用。
  • 派生类的指针可以隐含转换为基类的指针。
    在替代之后,派生类的对象就可以作为基类的对象使用,但只能使用从基类继承的成员。也就是说替代之后派生类仅仅发挥出基类的作用。
    派生类的构造和析构函数
    继承的目的是为了发展,只有通过添加新的成员,加入新的功能,类的派生才有实际意义。
    派生类的构造函数只对派生类新增的成员进行初始化,对所有从基类继承下来的成员,其初始化工作还是由基类的构造函数完成。
    构造派生类的对象时,需要对基类的成员对象和新增成员对象进行初始化。
    派生类的构造函数需要以合适的初值作为参数,其中一些参数要传递给基类的构造函数,用于初始化相应的成员,另一些参数需要用于对派生类新增的成员进行初始化。
class Derived:public Base2,public Base1,public Base3{
public:
	Derived(int a,int b,int c,int d):Base1(a),member2(d),member1(c),Base2(b){}
}

作用域分辨符

类名::成员名  //数据成员
类名::成员名(参数表)  //函数成员

如果派生类中声明了与基类成员函数同名的新函数,即使函数的参数表不同,从基类继承的同名函数的所有重载形式都会被隐藏。如果要访问被隐藏的成员,就需要使用作用域分辨符和基类名进行限定。
虚基类
将共同基类设置为虚基类,这时从不同的路径继承过来的同名数据成员在内存中就只有一个副本,同一个函数名也只有一个映射。
虚基类的声明是在派生类的定义过程中进行的,其语法形式为:

calss 派生类名:virtual 继承方式 基类名

多态性

多态是指同样的消息被不同类型的对象接收时导致不同的行为。
多态的类型
重载多态:普通函数和类的成员函数的重载,运算符重载。
强制多态:强制类型转换的例子,如整型数和浮点数的加法。
包含多态:类族中定义于不同类中的同名成员函数的多态行为,主要通过虚函数来实现。
参数多态:参数多态与类模板相关联,在使用时必须赋予实际从类型来实现。
多态的实现
编译时的多态和运行时的多态。
绑定工作在编译连接阶段完成的情况称为静态绑定。如重载、强制、参数多态。
绑定工作在程序运行阶段完成的情况称为动态绑定。如包含多态。(虚函数)
运算符重载
运算符重载的语法形式:

返回类型 operator 运算符(形参表)
{
	函数体
}

虚函数
主要用来解决第7章动态绑定的问题。可以通过虚函数利用一个循环结构一次处理同一类族中不同类的对象。虚函数经过派生之后,在类族中就可以实现运行过程中的多态。

virtual 函数类型 函数名(形参表);

虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。
虚函数实现运行时的多态要符合3个条件:
1.类之间满足赋值兼容规则
2.要声明虚函数
3.要由成员函数来调用或者是通过指针、引用来访问虚函数。
虚析构函数
C++中不能声明虚构造函数,但是可以声明虚析构函数。
虚析构函数的声明方法:

virtual ~类名();

如果一个类的析构函数是虚函数,那么由他派生出的所有子类的析构函数也是虚函数。
主要的应用:如果有可能通过基类指针调用对象的析构函数,就需要让基类的析构函数称为虚函数,否则会产生不确定的后果。
纯虚函数与抽象类
抽象类是带有纯虚函数的类。
纯虚函数是一个在基类中声明的虚函数,它在该基类中没有定义具体的操纵内容,要求个派生类根据实际需要给出各自的定义。
纯虚函数的声明格式:

virtual 函数类型 函数名(参数表) = 0//相比虚函数的声明,在格式上加了 =0

声明为纯虚函数后,基类中就可以不再给出函数的实现部分。
抽象类声明了一个类族的共同接口,而接口的完整实现,即纯虚函数的函数体,要由派生类自己定义。
抽象类不能实例化,即不能定义一个抽象类的对象,但是可以定义一个抽象类的指针和引用,可以通过该指针指向派生类的对象即可实现访问派生类对象的虚成员函数。
多态类型与基类指针
C++的类类型分为两类,多态类型和非多态类型。多态类型是指有虚函数的类类型,非多态类型是指所有的其他类型。
基类的指针可以指向派生类的对象。如果该基类是多态类型,那么通过该指针调用基类的虚函数时,实际执行的操作是由派生类决定的。

群体类和群体数据的组织

函数模板(参数多态)
参数化程序设计:通用代码应不受数据类型的影响,并且可以自动使用数据类型的变化。
参数多态:将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。

//函数模板的定义形式
template <模板参数表>
类型名 函数名(参数表)
{
	函数体的定义
}

template <typename T>
T abs(T x){
	return x<0? -x : x;
}
//在调用abs(n)时,编译器从实参的类型推导出函数模板的参数类型。这个过程叫做函数模板的实例化,该函数为该函数模板的一个实例。

通过函数模板产生的相关函数都是同名的,编译器通过重载的方法调用相应的函数。
类模板
使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、返回值或局部变量能取任意类型(包括系统预定义的和用户自定义的)。

//类模板的声明方法
template <模板参数表>
class 类名
{
	类成员声明
}
//如果要在类模板以外定义其成员函数,形式为
template <模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)
//当使用一个类模板建议对象时
模板名<模板参数表> 对象名1.对象名2

一个类模板声明自身并不是一个类,它说明了类的一个家族。只有当被其他代码引用时,模板才根据引用的需要生成具体的类。

迭代器
迭代器提供了顺序访问容器中每个元素的方法。
使用独立于STL容器的迭代器,需要包含头文件

int a[6]={1,2,3,4,5,6};
vector<int> b(a,a+5);
for(vector<int>::iterator it=b.begin();it!=b.end();++it)
{
	cout<<*it<<endl;
}

异常处理

#include <iostream>
using namespace std;
 
double division(float a,float b)
{
	if (!b)
	{
		throw "Error! 除数不能为0!";
	}
	cout << "【当b=0的时候,这里是不会执行的】" << endl;
	return a / b;
}
 
 
int main()
{
	float a, b;
	while(true)
	{
		cout << "Please enter A B : ";
		cin >> a >> b;
		try
		{
			cout << "A/B= " << division(a, b) << endl;
		}
		catch (const char *error) //char 对应于前方throw的类型
		{
			cout << error << endl;
		} 
		catch (...)    //捕获其他未知错误
		{
			cout << "Unknown Error !" << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值