(一) 递归算法
递归定义
程序直接或间接调用自身的编程技巧称为递归算法。递归算法是算法的基本。
它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
步骤如下:层层分解
递归使用
半数集问题:
int a[1001];
int comp(int n)
{
int ans=1;
if(a[n]>0)return a[n]; //已经计算
for(int i=1;i<=n/2;i++)
ans+=comp(i);
a[n]=ans; //保存结果
return ans;
}
注意:记忆式搜索
(二)复合数据类型
结构体类型(由多种成员构成的复合类型)
C++ 中的结构体是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。
定义样式
- 定义一
struct 类型名{
数据类型1 成员名1;
数据类型2 成员名2;
…
};
- 定义二
struct 类型名{
数据类型1 成员名1;
数据类型2 成员名2;
…
} 变量名;
实例如下:
struct student{
string name;
char sex;
int age;
double weight;
};
注意:由于结构体的不封闭性,在编程中少用结构体,尽量不用
枚举类型(由多种成员构成的复合类型)
枚举类型定义了一组命名的整数常量,以提高代码的可读性
需要注意的是:
1、枚举类型在必要时,如参与算术运算时,会被自动提升为算术类型
2、能表示枚举成员值的范围
3、枚举的成员名字是不可打印的,输出的是它所表示的整数值
string类型(C++的字符串类型)
标准库的string类
string 表示可变长度的字符序列
字符串是对象
注意:
定义与初始化
常用操作
其他操作
- string类的读写操作
C++中的iostream可以进行读写,需要注意的getline函数,getline(cin,line) - 判空
stringObj.empty()返回一个布尔值 - 获取string字符串的长度size()
- 赋值=,连接+
注意:
字符串字面值不是string类型,而是const char*类型
string对象和C风格字符串的转换:
1、可以将C风格的字符串直接赋给string对象,反之不可
2、用string对象的c_str()操作可以返回一个表示该string对象内容的C风格字符串,结果为const char*类型,即C风格字符串的首地址
指针类型(与对象地址相关的复合类型)
指针定义
指针持有一个对象的地址,称为指针“指向”该对象
通过指针可以间接操纵它指向的对象
指针的语法是:
指针基本概念:
指针类型
每个指针都有相关的类型,需要在定义指针时指出
type* pointer
==注意:==指针能保存非地址值,也不能被赋值或初始化为不同类型的地址值
空指针
指针值为0时是一个空指针,即不指向任何对象的指针
表示空指针的两种方法:
1. 0
2. 预处理常量NULL
存储空间的分配策略
静态(编译时)分配空间
编译器在处理程序源代码时分配内存;
效率高,灵活性差,运行前就要知道程序需要的内存大小和类型
动态(运行时)分配空间
程序运行时调用运行时刻库函数来分配内存;
占用程序运行时间,更灵活
动态存储空间管理
C++通过new和delete运算符进行动态存储空间的管理
- new运算符
new数组类型
new指针类型
- delete运算符
释放new分配的单个对象delete指针
释放new分配的数组delete[]指针
空悬指针:
执行delete运算后,指针ip指向的空间被释放,不能再使用ip指向的内存,但是ip这个指针变量自己 的存储空间不受影响
delete后的ip不是空指针,而是“空悬指针”,即指向不确定的单元
注意:delete之后,继续通过ip间接使用这个单元是非法的,会引起不可预料的运行错误
引用类型(与对象地址相关的复合类型)
引用定义
引用又称为别名,它可以作为对象的另一个名字;
注意:
- 引用一旦初始化,就不能只想其他对象
- 使用左值引用时
引用并非对象,是为已存在的对象所起的另一个名字
引用只能绑定到对象(有内存地址)上,不能与字面值或某个表达式的计算结果绑定在一起。
指针和引用
- const限定指针
const type* const cp = initAddressValue;
- 数组与指针
附加:
库函数中的begin()和end()
(三)类与对象
类的定义
类是对具有相同属性和行为的一类客观事物的概括描述。是用户自定义的数据类型(程序设计语言角度)
类的定义包括行为和属性两个部分:属性以数据表示,行为通过函数实现。
类的定义格式
注意:
类的数据成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。
类定义必须以分号“;”结束。
类成员的访问控制表
成员函数
类的成员函数是实现类的行为属性的成员。
一般将成员函数声明为函数原型,在类外具体实现成员函数。
对象
- 对象是类的实例或实体。
- 类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
对象的定义格式:
==注意:==必须在定义了类之后,才可以定义类的对象
指针访问格式:
类的应用实例
将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。 长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数” 。
#include<iostream>
using namespace std;
class CRectangle
{
public:
int w,h;
int Area() {
return w * h;
}
int Perimeter() {
return 2 * ( w + h);
}
void Init( int w_,int h_ ) {
w = w_; h = h_;
}
}; //必须有分号
int main( )
{
int w,h;
CRectangle r; //r是一个对象
cin >> w >> h;
r.Init( w,h);
cout << r.Area() << endl << r. Perimeter();
return 0;
}
类的定义与使用时应该注意
- 在类的定义中不能对数据成员进行初始化。
- 类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
- 类中的数据成员可以是C++语法规定的任意数据类型。
- 类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
- 类定义必须以分号“;”结束
- class与struct的不同:
class中,成员缺省情况是private。
struct中,成员缺省情况是public。
内联函数
成员函数重载
函数重载条件
构造函数和析构函数
构造函数
构造函数是用于创建对象的特殊的成员函数
当创建对象时,系统自动调用构造函数
默认构造函数:
如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象,默认构造函数形式:
类名::类名(){}
构造函数
- 不带参数:当用户没定义构造函数时,调用默认的构造函数;当用户定义了构造函数时,调用无参的构造函数(没有无参构造函数时要出错!系统不会调用默认构造函数)
- 带实参表:系统按照重载函数匹配原则,调用对应的构造函数;
构造函数的初始化列表(数据成员)
- 使用构造函数的函数体进行初始化
- 使用构造函数的初始列表进行初始化
注意:
构造函数的重载与调用
注意: 构造函数一般被定义为公有成员
析构函数
析构函数特点
- 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
- 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;
- 当撤消对象时,编译系统会自动地调用析构函数。
用delete语句实现析构函数
this指针
用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配存储单元 。
复制构造函数
深复制和浅复制
关于浅复制:
●在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一 资源的复制方式称为浅复制。
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容
●默认复制构造函数所进行的是简单数据复制,即浅复制
关于深复制:
●通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。
●自定义复制构造函数所进行的复制是浅复制。
类的其他成员
常成员
静态成员
友元
常成员
常数据成员
const int M; //说明常数据成员
Mclass() : M(5) { } //用初始式对常数据成员赋值
常对象
常成员函数
静态成员
类成员冠以static声明时,称为静态成员。
静态数据成员
class A
{
int n;
static int s;
};
类外静态数据成员
类型 类名::静态数据成员[=初始化值]; //必须进行声明
静态成员函数
说明:
- 静态成员函数在类外定义时不用static前缀。
- 静态成员函数主要用来访问同一类中的静态数据成员。
- 私有静态成员函数不能在类外部或用对象访问。
- 可以在建立对象之前处理静态数据成员。
- 编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
- 静态成员函数中是没有this指针的。
- 静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。
友元
友元函数
如果在本类(类A)以外的其他地方定义了一个函数(函数B)
这个函数可以是不属于任何类的非成员函数,
也可以是其他类的成员函数,
在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。
友元函数(函数B)可以访问这个类(类A)中的私有成员
友元类
假设有如下的类的
class B
{
……………
private:
A obj1;
};
A是另一个类,B类的成员函数要访问A的私有成员,此时,需要在A类中,把类B声明为A 的友元。
类的组合
类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类 “抄”进来。
当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;
类组合的简单应用
在学生信息管理的过程中, 需要知道每个学生的姓名,学号以及考试成绩可以定义一个学生类,数据成员包括name(string), number(string)
考试科目比较多
方法一:在类中增加新的数据成员
方法二,定义成绩类,采用类的组合解决问题
class Score
{
public:
Score(float c, float e, float m );
Score();
void show();
void modify(float c, float e, float m);
private:
float computer;
float english;
float mathematics;
};
class Student
{
private:
string name;
string stu_no;
Score score1;
public:
Student(string name1, string stu_no1, float s1, float s2, float s3):score1(s1,s2,s3){
name=name1;
stu_no=stu_no1;
}
~Student();
void modify(string name1, string stu_no1, float s1, float s2, float s3);
void show();
};
(四) 运算符重载
重载规则
不能重载的运算符
. :: .* ?: sizeof
可以重载的运算符
+ - * / % ^ & | ~
! = < > += -= *= /= %
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ‘ ->
[] () new delete new[] delete[]
运算符可以重载为友元函数
一元运算符和二元运算符
成员函数重载声明格式
友元函数重载
典型规则
数学类
- 设 A Aobject ;
运算符 ++和 - - 有两种方式:
前置方式: ++Aobject --Aobje - 重载函数原型为:
类名 & 类名 :: operator= ( 类名 ) ; - 重载运算符[]和()
运算符 [] 和 () 是二元运算符
[] 和 () 只能用成员函数重载,不能用友 元函数重载
注:部分内容
(五)学习感受
作业1
利用《数据结构》知识,定义一元素为整数的单链表类,并在主函数中调用各个功能:数据成员:指向头结点的指针、链表中当前元素的个数;成员函数:初始化、在尾部增加一元素、查询指定数据的元素、在指定元素前插入一新元素、删除指定元素、遍历链表中的元素、输出所有链表中的元素、将一个单链表逆序(选作)。
#include<iostream>
using namespace std;
class Link{
public:
Link(){}
Link(int a[],int k);//构造函数
void printfLink();//遍历链表中的元素
int getData(int n);//查询指定数据的元素
void addData(int x);//在尾部增加一元素
void insertData(int n,int x);//在指定元素前插入一新元素
void deleteData(int n);//删除指定元素
void invert();//将一个单链表逆序(选作)——没写出来
int getSize();//输出链表中元素的个数
private:
Link * next;//指向下一个节点
Link * head;//指向头节点
int n;//当前元素个数
int x;
};
//代码太多,此处不做展示
感受:做作业时半懂不懂的,照搬上学期数据结构内容,曲解老师意思。
作业2
定义一元素为整数的动态数组类,并在主函数中调用各个功能:数据成员:指向元素首地址的指针(用于存储动态申请的数组空间)、当前数组的容量、当前元素的个数;成员函数: 带参/无参构造函数(无参的动态申请10个元素空间并改变当前数组的容量、当前元素的个数值;带参的按参数处理); 在尾部增加一元素; 修改指定位置上的元素值; 查询指定数据的元素是否存在; 输出所有数组中的元素; 增加数组容量(动态申请新容量数组空间、将原来数组空间中的元素复制到新空间并修改当前数组的容量值,释放delete原来数组空间); 析构函数(释放动态申请的数组空间); 二分查询指定数据的元素并返回原始所处的位置(选作,首先在函数内定义一个结构体数组,临时存放当前元素值和位置。其次,使用sort函数对结构体数组排序,最后,利用二分(折半)查找方法找指定元素是否存在。存在,返回原始位置值。否则,返回-1。返回前释放结构体数组的空间)。
#include<bits/stdc++.h>
using namespace std;
class ArrayList
{
public:
ArrayList();//无参构造函数
ArrayList(int x);//带参构造函数
void addData(int data);//在尾部增加一元素
void modify(int k,int data);//修改指定位置上的元素值
bool isExit(int data);//查询指定数据的元素是否存在
void printf();//输出所有数组中的元素
void addSpace();//增加数组容量
int indexOf(int data);//二分查询指定数据的元素并返回原始所处的位置
~ArrayList();//析构函数
private:
int*p;//指针形式数组 指向元素首地址的指针
int size;//当前数组的长度
int n; //当前元素的个数
};
//代码太多,此处不做展示
感受:理解的逐步加深,在作业过程中四处碰壁,其中二分查询费很大劲,查资料,问同学。毛病:自身拖延症问题太严重,要改正。
作业3
设计并实现简单通讯录系统1.定义描述联系人信息的类(数据类) 数据成员:身份证号(常成员)、姓名、手机号(用字符串数组,可能不止一个)、QQ号、微信号、住址; 成员函数:构造函数若干、get/set函数、显示函数。2.定义功能实现类(操作类) 数据成员:联系人数组(链表也可)、联系人数量、数组容量; 成员函数: 构造函数(完成对本类数据成员的初始化); 增加联系人(在尾部增加); 按身份证号查询联系人并输出找到的联系人信息。找不到,不做任何操作; 按身份证号修改联系人信息(找到,修改全部其他数据。否则,不做任何操作); 按身份证号删除联系人信息; 按“姓”(如姓李)查找所有满足条件的联系人并逐一输出; 析构函数(可不要)
#include<bits/stdc++.h>
#include<string>
using namespace std;
//数据类
class Person
{
public:
Person(string i,string c,string n[],string q,string w,string a);//带参构造函数
Person(string c,string n[],string q,string w,string a);//带参构造函数
Person(string i,string c,string n[]);//带参构造函数
Person(string idNum);//带参构造函数
Person(){};//无参构造函数
string getIdNum();//获取身份证号
string*getCpNum();//获取手机号
string getName();//获取姓名
string getQqNum();//获取QQ号
string getWcNum();//获取微信号
string getAddress();//设置地址
void setIdNum(string i);//设置身份证号
void setCpNum(string n[]);//设置手机号
void setName(string i);//设置姓名
void setQqNum(string i);//设置QQ号
void setWcNum(string i);//设置微信号
void setAddress(string i);//设置地址
void show();//显示函数
private:
string idNum;//身份证号
string name;//姓名
string cpNum[10];//手机号
string qqNum;//QQ号
string wcNum;//微信号
string address;//地址
};
//代码太多,此处不做展示
感受:此次任务真可谓工程浩大,完成的吃力,任务完成后对类有一种明朗的感觉。
整体感受
- 压力即动力。通过这段时间的学习,真心觉得老师讲的不错,感谢老师的严苛,压力越大,动力越大。
- 付出与收获成正比。 由于网课原因,上课容易走神,下课就要花时间来弥补,容易形成恶性循环,要牢记一份耕耘,一份收获,你欠下的,总归要补的。
- 认清重点。c++是计算机语言的重中之重,虽然有过对Java内容的学习,可以加以类比,但是c++相比于其他语言,个人感觉还是不简单的,后续应该积极适应,热爱生活。
- 享受收获。不应该只是觉得很难,而是享受在困难的道路上摸索的收获,就像开始说的那样,希望自己有一个较大的提升,不仅仅是入门,更需要自我探索。