C++程序设计1~6周学习总结

递归算法

程序直接或间接调用自身的编程技巧称为递归算法(Recursion)。
直接或间接调用自身的函数称为递归函数
它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归的关键在于找出递归定义和递归终止条件
**递归定义:**使问题向边界条件转化的规则。递归定义必须能使问题越来越简单。
**递归终止条件:**也就是所描述问题的最简单情况,它本身不再使用递归的定义。

递归算法解题通常有三个步骤:
1)分析问题、寻找递归:找出大规模问题与小规模问题的关系,这样通过递归使问题的规模逐渐变小。
2)设置边界、控制递归:找出停止条件。
3)设计函数、确定参数:设计函数体中的操作及相关参数。

递归思想——层层分解
递归问题主要是:将大规模问题分解为小规模问题(层层分解)逐渐变小,直到满足停止的条件!
例:n!=n*(n-1)! ——停止条件是当分解到1或者2时停下。

复合数据类型

结构体类型

结构体类型的声明格式如下:
struct 类型名{
数据类型1 成员名1;
数据类型2 成员名2;

} ;

struct 类型名{
数据类型1 成员名1;
数据类型2 成员名2;

} 变量名;

结构体变量具有以下特点:
(1)可以对结构体变量的整体进行操作。
例如:swap(a[i],a[j])
(2)可以对结构体变量的成员进行操作。
引用结构体变量中成员的格式为:
结构体变量名. 成员名
(3)结构体变量的初始化方法与数组类似。

枚举类型

枚举类型定义了一组命名的整数常量,以提高代码的可读性
enum TrafficLight { red, green, yellow };
TrafficLight枚举类型定义了3个常量:0,1,2 分别和名字red,green以及yellow关联。
TrafficLight是一个枚举类型,可以用来定义枚举变量,变量的值只能是枚举成员。
TrafficLight stop = red;

可以指定枚举成员的值
编译器会给未指定值的枚举成员赋予相邻值的下一整数值

enum ShapeType{circle=10, square=20, rectangle};
//rectangle成员的值是21

可以使用未命名的枚举类型定义常量

enum {False, True};
//定义了两个常量False和True,值分别是0和1

注意:
枚举类型在必要时,如参与算术运算时,会被自动提升为算术类型
枚举的成员名字是不可打印的,输出的是它所表示的整数值

string

string 表示可变长度的字符序列
字符串是对象

string 类支持字符串对象的各种操作:
1、各种初始化方式
2、字符串之间的复制、比较、连接
3、查询字符串长度和判断字符串是否为空
4、访问字符串中的单个字符

使用string 类要包含头文件

初始化string对象的方式:
1、string s1 //默认初始化,s1是一个空串
2、 string s2(s1) //s2是s1的一个副本
3、string s2=s1 //等价于 string s2(s1)
4、string s3(“value”) //s3是字面值“value"的副本,除了字面值最后的空字符外
5、string s3=“value” // 等价于s3(“value”)
6、string s4(n,‘c’) // 把s4初始化为由连续n个字符c组成的串

string的常用操作:
os<<s 将s写到输出流os中,返回os
is>>s 从输入流is读取字符串赋值给s,字符串以空白分割,,返回is
getline(is,s) 从输入流is中读取字符串赋值给s,返回is
s.empty() s为空返回true,否则返回false
s.size() 返回s中字符的个数
s[n] 返回s中第n个字符的引用,位置n从0开始
s.c_str() 转换,返回s中内容对应的c风格字符串的首地址
s1=s2 赋值,用s2的副本替换s1原来的内容
s1+s2 连接,返回s1和s2连接后的内容
s1+=s2 追加,把s2的内容追加到s1的后面
s1==s2 如果s1和s2的内容完全相同,则相等
s1!=s2 string对象的比较对字符区分大小写
<,<=,>,>= 利用字符在字典中的顺序进行比较,区分大小写

可以用循环读取未知数量的string对象
输入操作符返回输入流对象,如果输入流对象处于有效状态,表示没有遇到文件结束或非法输入

//读取输入流中的单词,直到文件结束
string word;
while(cin >> word)
cout << word << endl; 
``

getline()函数:
两个参数:输入流对象和存放读入字符串的string对象
从指定输入流中读取内容,遇到换行符为止;将所读内容存入指定的string对象中,流中的换行符被读取并丢弃
返回参数输入流对象

//每次读取一行文本,直到文件结束
string line;
while(getline(cin, line))
cout << line << endl;

判断stirng对象是否为空
empty()函数判断string对象是否为空,返回一个布尔值
stringObj.empty()

//每次读取一行文本,输出非空的行
string line;
while(getline(cin, line))   
  if(!line.empty())  //line不为空,输出
    cout << line << endl; 

获取string对象的长度
size()函数返回string对象的长度,即对象中字符的个数
返回的长度是string::size_type类型

//每次读取一行文本,输出长度超过80个字符的行
string line;
while(getline(cin, line))
  if(line.size() > 80)
   cout<<line<<endl;
//字符串长度的类型
string line = "hello";
string::size_type len1 = line.size(); 
auto len2 = line.size();
decltype(line.size()) len3 = 0;

比较string对象:
可以用关系运算符比较两个字符串对象
两个string相等意味着它们的长度相同,并且所包含的字符也完全相同

string s1 = "hello";  
string s2 = "hello world"; // s2 > s1
string s3 = "Hello";  // s3 < s1, s3 < s2

string对象的赋值和连接
允许把一个string对象的值赋给另一个string对象
也可以为string对象赋一个字符串字面值常量

string s1 = "hello", s2;
s2 = s1;
s1 = "C++ Programming Language";

随机访问string中的字符
用下标运算符可以访问string对象中指定位置的字符
string对象s的下标范围从0到s.size-1

//将s中的第一个词改成大写形式
string s = "Hello, World!";
int index = 0;
while (index != s.size() && !isspace(s[index])) {
s[index] = toupper(s[index]);
++index;
}
cout << s << endl;  //输出:HELLO, World!

指针

指向一个对象,并持有该对象的地址。访问对象——地址访问。
典型用法:
构建链式的数据结构:例如链表和树;
管理程序运行时动态分配对象;
作为函数的参数。(用此可以更改参数值,还可以 引用 来更改)

int  *pi;
int* pi; 
char *pc1, *pc2;
char* pc1, pc2; 
char *pc1, pc2; 
char* pc1, *pc2;

同类型的指针可以进行“==”和“!=”的比较,返回bool类型;
指针可以进行+或-整数值的算术运算;
数组元素访问时可以用自增自减运算。

指向对象的指针
指向一个对象的指针有两个存储单元与之相关
一个是指针自己的存储单元,里面存放着所指对象的地址;
另一个就是指针指向的对象的存储单元,里面存放该对象的值。
可以定义存放指针对象的地址的指针

int ival = 1024;
int *pi = &ival;
int **ppi = &pi;  //ppi是指针的指针,存放pi的地址

空指针
指针值为0时是一个空指针,即不指向任何对象的指针
表示空指针的2种方法:
0
预处理常量NULL

// 生成空指针的2种方法 
int *p1 = 0;       
int *p2 = NULL; //不能写成下面的样子:
int zero = 0;
int *p4 = zero;

通用指针void
可以持有任何类型的地址值,即通用指针
相关的值是个地址,但是该地址保存的对象类型不知道
不能操纵void指针指向的对象,只能传送该地址值或者和其他地 址值进行比较
不允许void指针到其他类型指针的直接赋值

int a = 10;
char ch = 'k';
void* pv = &a;  //是否正确?
pv = &ch;   //是否正确?
int* pi = pv;   //是否正确?

动态分配空间 new、delete
new运算符
在堆上动态分配,并返回地址。

int* ip1 = new int; //在堆上分配一个int类型的对象,返回它的地址
*ip1 = 512;  //堆上分配的这个对象只能通过指针间接操作
int* ip2 = new int(100);  //在堆上分配一个int对象,初始化为100,返回其地址

delete运算符

int* ip = new int; ... //不再使用这个int对象时,释放内存
delete ip; //释放指针指向的int对象,将空间归还给动态存储区

const 限定指针

  1. 指向const对象的指针
//指针的值不能改变
const type *p1;
const type *p2;
  1. 指向非const对象的const指针
type* const p=a;
 //初始化后值不能改变,指向固定的单元;
  1. 指向const 对象的const指针
const type* const p=a;
//地址不可变,地址所包含内容也不可变

引用

引用没有分配空间,只是在绑定变量(变量的另一个名字),主要用做函数的参数。
& 说明符
引用必须被初始化,初始值是一个有内存地址的对象。(不是简单的拷贝,而是绑定在一起)

type& 引用名 =初始值;
//x=y
int  x=10,y=20;
int &r=x;
r=y;

类和对象

类的定义

类是对具有相同属性和行为的一类客观事物的概括描述。是用户自定义的数据类型(程序设计语言角度)
类的定义包括行为和属性两个部分。属性以数据表示,行为通过函数。

C++类定义的格式

class  类名
{
   public:
      公有数据成员和成员函数;
   protected:
      保护数据成员和成员函数;
   private: 
     私有数据成员和成员函数;
}// 注意分号不能省略!!!
各成员函数的实现

注意事项:

  1. 类的数据成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。
  2. 类定义必须以分号“;”结束。
  3. 类与结构体的区别:
    没有明确指定类成员的访问权限时,C++结构体的成员是 公有的,而类的成员是私有的

访问控制

public 公有成员 类的外部接口
protected 保护成员 仅允许本类成员函数及派生类成员函数访问
private 私有成员 仅允许本类成员函数访问

成员函数

类的成员函数是实现类的行为属性的成员。
一般将成员函数声明为函数原型,在类外具体实现成员函数。

成员函数的定义:

返回值类型  类名::成员函数名(参数表)
{
  函数体
}

对象

对象的定义

对象是类的实例或实体。
类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
对象的定义格式如下 :

类名  对象名1,对象名2,…,对象名n;

定义对象时注意要点

必须在定义了类之后,才可以定义类的对象。

对象成员的访问形式

圆点访问形式:对象名.公有成员
指针访问形式:对象指针变量名->公有成员

类定义和使用时注意要点

  1. 在类的定义中不能对数据成员进行初始化。
  2. 类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
  3. 类中的数据成员可以是C++语法规定的任意数据类型。
  4. 类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
  5. 类定义必须以分号“;”结束
  6. class与struct的不同:
    class中,成员缺省情况是private。
    struct中,成员缺省情况是public。

内联函数

内联函数作用

减少频繁调用小子程序的运行的时间开销

内联函数机制

编译器在编译时,将内联函数的调用以相应代码代替

内联函数声明

内联函数声明:inline 函数原型
注:内联函数仅在函数原型作一次声明。
适用于只有1 ~ 5行的小函数
不能含有复杂结构控制语句 ,不能递归调用

函数重载

函数重载:函数名相同,但参数不相同(类型不同,或者个数不同)的一组函数。
编译器根据不同参数的类型和个数产生调用匹配
函数重载用于处理不同数据类型的类似任务

构造和析构函数

默认构造函数

如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象,默认构造函数形式:

类名::类名(){} 

构造函数的初始化列表

格式:

funname(参数列表):初始化列表
{函数体,可以是空函数体}

初始化列表的形式:

成员名1(形参名1),成员名2(形参名2),成员名n(形参名n) 

类成员的初始化的顺序:
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关

带默认值的构造函数

#include <iostream>
using namespace std;
class Box
{
public:
     Box();  //定义了全部带默认值的构造函数,不能再定义无参   构造函数
 Box(int h=10,int w=10 , int l=10); //只能在声明时指定默认值
 int volume();
private:
 int height,width, length;
};
Box::Box(int h, int w,int l):height(h),width(w),length(l)
{}
int Box::volume(){
 return width*length*height;
}
int main()
{
 Box box1;
 cout<<"The volume is "<<box1.volume();
 Box box2(12,30,25);
 cout<<"The volume is "<<box2.volume();
    Box box3(30,25);
 cout<<"The volume is "<<box3.volume();
 return 0;
}

析构函数

对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间,析构函数可以完成上述工作。
析构函数自动调用(隐式调用)
析构函数没有返回值,不能有参数,也不能重载

定义格式如下(类外实现):

类名::~类名()
{
       函数语句
} 

析构函数有以下一些特点:

① 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);

② 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;

③ 当撤消对象时,编译系统会自动地调用析构函数。

this指针需要显示使用this指针的三种情况:

(1)在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用 return *this,返回本对象的地址时,return this。
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。
(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。

复制构造函数

复制构造函数用一个已有同类对象创建新对象进行数据初始化,C++为类提供默认版本的复制构造函数。
程序员可以定义用户版本的复制构造函数

语法形式

类名 :: 类名(const  类名  &  引用名  ,  …);

特点:

(1)复制构造函数名与类名相同,并且也没有返回值类型。

(2)复制构造函数可写在类中,也可以写在类外。

(3)复制构造函数要求有一个类类型的引用参数。

(4)如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。

以下三种情况下由编译系统自动调用复制构造函数:
(1)声明语句中用类的一个已知对象初始化该类的另一个对象时。
(2)当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
(3)当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。

浅复制与深复制

关于浅复制:
●在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容
●默认复制构造函数所进行的是简单数据复制,即浅复制

关于深复制:
●通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。
●自定义复制构造函数所进行的复制是浅复制。

类的其他成员

常成员

常数据成员是指数据成员在实例化被初始化后,其值不能改变。

在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数

常对象

如果在说明对象时用const修饰,则被说明的对象为常对象。

常对象的说明形式如下:

类名 const 对象名[(参数表)];

const  类名 对象名[(参数表)];

在定义常对象时必须进行初始化,而且不能被更新。

说明:

(1)C++不允许直接或间接更改常对象的数据成员。

(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。

常成员函数

在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:

类型说明符 函数名(参数表) const;

const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。

常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)

静态成员

类成员冠以static声明时,称为静态成员。
静态数据成员为同类对象共享。
静态成员函数与静态数据成员协同操作。
静态成员不属于某一个单独的对象,而是为类的所有对象所共有。
静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员。
对于类的普通数据成员,每一个对象都各自拥有一个副本。(分配不同的存储空间)
对于静态数据成员,每个类只拥有一个副本 。(在静态存储区分配一个存储空间,对所有对象都是可见的)
公有访问权限的静态成员,可以通过下面的形式进行访问:
(1)类名::静态成员的名字
(2)对象名.静态成员名字
(3)对象指针->静态成员的名字
(4)在静态成员函数内部,直接访问。
静态数据成员声明及初始化:
在类外进行静态数据成员的声明:

类型 类名::静态数据成员[=初始化值];   //必须进行声明

不能在成员初始化列表中进行初始化

如果未进行初始化,则编译器自动赋初值(默认值是0)

初始化时不能使用访问权限

静态成员函数

除静态数据成员以外,一个类还可以有静态成员函数。
静态函数仅可以访问静态成员,或是静态成员函数或是静态数据成员。
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
静态成员函数没有this指针,只能对静态数据操作
定义静态成员函数的格式如下:

static 返回类型 静态成员函数名(参数表);

与静态数据成员类似,调用公有静态成员函数的一般格式有如下几种:

(1)类名::静态成员函数名(实参表)

(2) 对象. 静态成员函数名(实参表)

(3)对象指针->静态成员函数名(实参表)

友元函数

如果在本类(类A)以外的其他地方定义了一个函数(函数B),这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数,在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。

友元函数(函数B)可以访问这个类(类A)中的私有成员

class  A
   { private:
          int  i ;
          friend void FriendFun(A * , int) ; 
      public:
         void MemberFun(int) ;
   } ;void FriendFun( A * ptr , int x  ) 
     { ptr -> i = x ; } ;  
void A:: MemberFun( int x ) 
     { i = x ; } ;
     
//作用相同
A Aobj ;
FriendFun( &Aobj, 5 ) ;
Aobj.MemberFun( 5 ) ;

友元类

假设有如下的类:

class B
{ 
    ……………
  private:
 A obj1;
};

A是另一个类,B类的成员函数要访问A的私有成员,此时,需要在A类中,把类B声明为A的友元。
若F类是A类的友元类,则F类的所有成员函数都是A类的友元函数

友元类通常设计为一种对数据操作或类之间传递消息的辅助类

友元类的声明格式:

friend class 类名; 

运算符重载

运算符重载规则

  1. 不改变运算符的优先级
  2. 不改变运算符的结合性
  3. 不改变运算符所需要的操作数
  4. 不能创建新的运算符

用成员或友元函数重载运算符

用成员函数重载运算符
1.一元运算符
Object op 或 op Object
重载为成员函数,解释为:
Object . operator op ()
操作数由对象Object通过this指针隐含传递
重载为友元函数,解释为:
operator op (Object)
操作数由参数表的参数Object提供
2.二元运算符
ObjectL op ObjectR
重载为成员函数,解释为:
ObjectL . operator op ( ObjectR )
左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递
重载为友元函数,解释为:
operator op ( ObjectL, ObjectR )
左右操作数都由参数传递

成员运算符函数的原型在类的内部声明格式如下:

class X {
    //…
返回类型 operator运算符(形参表);
  //…
}

在类外定义成员运算符函数的格式如下:

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

双目运算符重载为成员函数
对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。

单目运算符重载为成员函数
对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。

一般而言,采用成员函数重载单目运算符时,以下两种方法是等价的:

 @aa;                // 隐式调用
 aa.operator@();    // 显式调用

成员运算符函数operator @所需的一个操作数由对象aa通过this指针隐含地传递。因此,在它的参数表中没有参数。

用友元函数重载运算符
友元函数重载运算符常用于运算符的左右操作数类型不同的情况
例如:

class   Complex
{     int    Real ; 
      int    Imag ;
   public :
       Complex ( int a ) { Real = a ;   Imag = 0 ; }     
       Complex ( int  a  , int  b ) { Real = a ;   Imag = b ; }
       Complex  operator + ( Complex ) ;...
} ;   
int   f ( )
{ Complex  z ( 2 , 3 ) ,   k ( 3 , 4 ) ;
   z = z + 27 ;  //z . operator + ( 27 ) OK
   z = 27 + z ;  //27 . operator + ( Z ) NO...
}

在第一个参数需要隐式转换的情形下,使用友元函数重载运算符是正确的选择

友元函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换

C++中不能用友元函数重载的运算符有:
= () [] ->

成员运算符函数与友元运算符函数的比较:
(1) 成员运算符函数比友元运算符函数少带一个参数(后置的++、–需要增加一个形参)。
(2) 双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。
运算符 [] 和 () 是二元运算符
[] 和 () 只能用成员函数重载,不能用友元函数重载

几个典型运算符重载

重载赋值运算符

赋值运算符重载用于对象数据的复制
operator= 必须重载为成员函数
重载函数原型为:
类名 & 类名 :: operator= ( 类名 ) ;

重载运算符[]和()

运算符 [] 和 () 是二元运算符
[] 和 () 只能用成员函数重载,不能用友元函数重载

重载下标运算符

[] 运算符用于访问数据对象的元素
重载格式 类型 类 :: operator[] ( 类型 ) ;


设 x 是类 X 的一个对象,则表达式
x [ y ]
可被解释为
x . operator [ ] ( y )

重载函数调用符 ()

() 运算符用于函数调用
重载格式 类型 类 :: operator() ( 参数表 ) ;


设 x 是类 X 的一个对象,则表达式
x ( arg1, arg2, … )
可被解释为
x . operator () (arg1, arg2, … )

重载流插入和流提取运算符

istream 和 ostream 是 C++ 的预定义流类
cin 是 istream 的对象,cout 是 ostream 的对象
运算符 << 由ostream 重载为插入操作,用于输出基本类型数据
运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据
用友元函数重载 << 和 >> ,输出和输入用户自定义的数据类型

重载输出运算符“<<”(只能被重载成友元函数,不能重载成成员函数)
定义输出运算符“<<”重载函数的一般格式如下:

ostream& operator<<(ostream& out,class_name& obj)
    {
          out<<obj.item1;
          out<<obj.item2;
          .. .
          out<<obj.itemn;
          return out;
    } 

重载输入运算符“>>” (只能被重载成友元函数)

定义输入运算符函数 “>>”重载函数的一般格式如下:

istream& operator>>(istream& in,class_name& obj)
    {
            in>>obj.item1;
            in>>obj.item2;
            . . .
            in>>obj.itemn;
            return in;
    } 

感想

C++程序设计就好比一本武功秘籍中的一个个基本招式,只有把每个基本的招式学好学会才能练好武功,成为一个大侠。所以学习好C++程序设计是非常重要的,我们只有掌握了每个知识点并熟练运用才能真正地学好。在这六周的学习中,感觉自己学的还好,老师讲的知识点都可以理解,在此基础上,我也会多多练习,毕竟熟能生巧嘛。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值