【面向对象编程 C++】笔记(完结)

前言

是为复习做的笔记。
内容来自课本和老师的课件。
不全面。

第10章 类和对象

在这里插入图片描述
面向对象:注重过程,把事件分成小模块。

类和对象的定义与访问

在这里插入图片描述
注意:类定义结束处有分号

类是一种类型,该类型的变量成为对象

类成员的访问特性:
在这里插入图片描述

成员函数的定义

类内声明:
在这里插入图片描述
类外定义:

返回值类型 类名::函数名 (函数参数)

在这里插入图片描述

对象的存储空间和定义的说明

1、定义对象后,对象数据成员占用不同的存储空间,但所有对象的同一成员函数共享同一代码空间。

2、自身对象不可以作为自身类的数据成员。

如图,非法:
在这里插入图片描述
3、类的说明:

class A;//A类的说明

4、类的定义方法:
有三种,但常用的只有一种。
定义完类后定义对象:

在这里插入图片描述
5、结构体与类:
结构体成员缺省的存取权限:共有;
类:私有。
结构体是类的特例。

6、类的成员的初始化
公有成员的初始化:
在这里插入图片描述

构造函数与析构函数

构造函数:
构造函数名与类名相同。
在类外定义构造函数:

ClassName::ClassName(<形参>)
{......}

构造函数调用时机:在产生新对象时自动调用。

构造函数特点:
1、也是成员函数,一般定义为公有
2、不给出返回值。
3、函数名与类名相同。
4、可重载。
5、创建对象时自动调用。

析构函数:
定义析构函数的格式:
类内:

~ClassName(){......}

类外:

~ClassName::ClassName()
{......}

调用时机:撤销对象时自动调用。

析构函数特点:
1、成员函数,一般公有。
2、定义时前加~。
3、不指定返回值类型,无参数
4、不可重载
在这里插入图片描述
注意:若在创建对象时有new,则在析构函数里必须要写delete;

动态申请的对象——使用new产生对象时,系统自动调用构造函数;使用delete撤销对象时,自动调用析构函数。

构造和析构的缺省、拷贝构造函数

缺省构造函数:
定义类时没定义构造函数,编译器会自动生成如下函数:

ClassName::ClassName() {};

若已经定义了构造函数(不论是否缺省),编译系统都不会自动生成缺省构造函数了。

缺省构造函数只能有一个。

如图:这两个都定义了就是错误的重载。因为当定义一个不带参数的对象时,编译器就不知道调用哪个函数。
在这里插入图片描述
缺省析构函数:
编译器自动产生的缺省析构函数:

ClassName::~ClassName() {};

拷贝构造函数:
用一个已知的对象来初始化一个被创建的同类对象。

格式:
参数是同类对象的引用

ClassName::ClassName(const ClassName&Obj)

若用户不定义拷贝构造函数,则系统自动产生一个缺省的拷贝构造函数。

注意!!

当类中有动态申请的数据空间时,必须定义拷贝构造函数,否则会出错。
因为缺省的拷贝构造函数是浅拷贝,若它指向的内存被释放了,整个程序就出错了。
而自己定义的拷贝构造函数就可以直接new一个内存来存放相同的(指向的)内容,这样即使原来内存被释放,也不会出问题。

在这里插入图片描述
拷贝构造函数的调用时机:
初始化。括号和等号作用一样的。
在这里插入图片描述

内联函数和外联函数

在这里插入图片描述
在类外定义内联函数的方法:在类名前加inline;

inline ClassName::FunctionName(参数)

内联函数的优点:减少时间开销。

(感觉也没啥用)

this指针

在这里插入图片描述
类的成员函数是共有的。因为有this指针,所以会用某个对象的函数。

其他

1、定义一个类时,可以把已定义的类的对象作为该类的成员。即类的成员可以是其他类的成员。
2、新对象产生时的初始化是通过该类的构造函数来实现的。
3、如果是套娃的类,则是:先调用对象成员的构造函数,再调用自身的构造函数。
4、析构函数的调用顺序与对象产生的顺序相反。(最先定义的最后析构)

第11章 类和对象的其他特性

静态成员

一个类的不同对象,其数据成员的存储空间是相互独立的。
若有一个成员定义为静态的,则该类的所有对象的该成员共用同一存储空间
即,静态数据成员属于类,不属于某个对象。
为了解决对象之间的数据共享问题的。

静态成员的定义:

static 数据类型 变量名

在这里插入图片描述
静态成员的初始化:
1、只能赋值一次
2、必须在类外初始化。

数据类型 ClassName::变量名 = 值;//初始化

如:

int Sample::d=10;

静态成员函数

1、静态成员函数属于
2、可用类名/对象名调用。
3、静态成员函数中能直接访问静态数据成员。

友元

类具有封装性和信息的隐蔽性;只有类内可以访问私有成员,在类外要通过公有函数接口才能访问私有成员。
但是,若是某个函数是类的友元函数,这样就可以直接访问该类的私有成员。

如:

class A
{
	...
	friend void fun();
};
	void fun(){};

在类内声明友元函数:

friend 数据类型 友元函数名(参数)

慎用友元,因为友元破坏了类的数据成员的隐蔽性

注意:
1、友元函数不是类的成员函数。因此,友元函数的声明可以放在任何位置:public\private\protected;
2、慎用友元函数。
3、一个类的成员函数可作为另一个类的友元函数,一个类可以作为另一个类的友元:此时,该类的所有成员函数都是另一个类的友元函数。

第12章 继承和派生

概念:已知一个类A,对A拓展得到B(增加属性和行为),则称B继承了A,或称A派生了B。

单一继承

在这里插入图片描述
公有继承:
只有public是可以访问的。
若是想访问基类的私有数据,则可以在基类定义公有函数接口访问。
在这里插入图片描述

私有继承:
B的类内可以访问A的public和protect。
在这里插入图片描述
保护继承:
B的类内可以访问A的public和protect。
在这里插入图片描述
注意:
1、后两者用的不多。
2、其实只有公有继承是真正的继承,因为没有改变成员的属性。

private和protected的区别:
在这里插入图片描述
总结:
在这里插入图片描述
在这里插入图片描述

多重继承

逗号分隔。
在这里插入图片描述

多重继承相关

构造和析构的顺序
在这里插入图片描述
在这里插入图片描述

二义性和支配规则

若AB类中都有数据成员x,且C类多重继承AB,就会有二义性
解决方法:
用A::x表示A类继承的x,B::x表示B类继承的x。
同名成员函数的访问也同理。

多重继承的冲突
当把派生类作为基类,又派生出新的派生类时,这种限定作用域的运算符不能连续嵌套使用,如下形式的使用方式是不允许的:

<类名1>::<类名2>::<类名3>... ::<成员名>

虚基类
在这里插入图片描述
A是基类,23两种virtual方法都可以。
在这里插入图片描述
注意:如果派生类有多个基类,有些是虚基类有些是非虚基类。则基类构造的调用顺序是先调用虚基类的构造函数再调用非虚基类的构造函数,并按照继承顺序调用。

另:继承A与类中有A的对象是不同的。
继承A是类有A的属性,而类中有A的对象说明此类包含A的对象。
如:有一个点类,一个圆类。因为圆包含圆心,所以这里应该是圆类有点累的对象作为数据成员。

第13章 多态性

在C++中,多态性分为两种:静态多态、动态多态。

函数重载和运算符重载属于静态多态。
函数重载:相同函数名可以完成不同功能。
运算符重载:相同运算符完成不同功能。

动态多态是指:程序执行过程中确定的关系,
如动态确定函数的调用关系。(**翻译:**在函数执行的时候确定调用哪一个函数,静态的是在编译的时候就确定好了)

动态多态(亦即运行时多态)是通过类的继承和虚函数来实现的。

运算符的重载

重载运算符的限制
(1)只能对已有运算符重载,不可臆造新的运算符。
(2)不允许改变运算符的优先级和结合性
(3)不允许改变运算符的语法结构
如二元运算符只能重载成二元运算符,一元运算符只能重载成一元运算符。

在类内定义运算符重载的函数格式:

<函数返回值类型>operator<重载运算符>(参数列表)
eg:
A operator +(const A&a) //返回一个类

在类外定义运算符重载的函数格式:

<函数返回值类型><类名>::operator<重载运算符>(参数列表)
eg:
A A::operator +(const A&a)

将其重载为友元函数
类内:

friend <函数返回值类型>operator<重载运算符>(参数列表)
eg:
friend Complex operator +(const Complex &c1)

类外:

<函数返回值类型>operator<重载运算符>(参数列表)

类内声明类外定义?

两种重载方式的比较:
在这里插入图片描述
比较的总结:
对一般的二元运算符重载友元函数比重载为成员函数更优越
但是对于赋值运算符,将其重载为成员函数,因为赋值运算符是一个二元运算符,其语法格式为

<变量>=<表达式>

第一个运算量必须是对象(变量也是对象),通过对象调用成员函数比较自然。
若重载为友元,则可能会出现5.6=c这样的表达式,与赋值表达式的语义不一致

即,二元重载,友元,防止5.6(C);
赋值重载,成员,因为要c=5.6;

若运算符重载函数返回对象值,有两种写法:

//1
Complex Complex::operator+=(const Complex &c)
{
	r+=c.r;
	i+=c.i;
	return *this;//!!返回我自己
}

//2
Complex Complex::operator+=(const Complex &c)
{
	r+=c.r;
	i+=c.i;
	Complex temp=*this;
	return temp;//!!也是返回我自己
}

但要注意,因为返回对象值时需要调用拷贝构造函数初始化内存临时对象,因此若对象有动态分配的存储空间,就必须定义拷贝构造函数。
第3、4个例子没看懂,在ppt的26、27

结论:
1、返回本类对象时,可以用对象自身和局部对象做为返回值(有时需要定义拷贝构造函数)。
2、返回对象的引用时,不能用局部对象做为返回值。

其他运算符的重载:
前置++的运算符成员函数一般格式:

<函数返回值><类名>::operator++()

后置

<函数返回值><类名>::operator++(int)

是规定,没有啥具体去辨别原因。

前置++,友元:

friend<函数返回值>operator++(<类名>&obj)

后置++,友元:

friend<函数返回值>operator++(<类名>&obj,int)

字符串类
见例题Li1318.

运算符重载函数小结

1、运算符重载函数可以通过成员函数也可以通过非成员函数实现,非成员函数包括友元函数
2、只能重载为成员函数的运算符有:=、[]、()、->, new、delete以及“类型转换运算符”如: operator int()等。
3、只能重载为友元函数的运算符有:>>,<<。此规则适用于编程者定义的新类。参阅14.5节(重载插入和提取运算符)。
4、运算符重载函数的形参不允许有默认值。因为形参对应参加运算的的实际的运算量,运算时运算量是不能缺省的。
5、运算符重载函数的形参至少有一个导出数据类型的量。
6、C++中唯一不能被派生类继承的是
赋值运算符重载函数。

7、如果用户未显式定义赋值运算符重载函数,编译器会自动生成一个,完成数据成员的简单赋值。

类型转换函数

类型转换函数只能用成员函数实现,不能用友元函数实现。

静态联编与动态联编

静态
1、联编是指一个计算机程序彼此关联的过程。
2、在本章中指函数间调用关系的确定
3、按照联编所确定的时刻不同,可分为两种:静态联编和动态联编。
4、静态联编是指联编出现在编译连接阶段,即函数调用关系的确定是在程序执行之前。
5、种联编又称早期联编,通过这种联编可实现静态多态

动态
1、运行阶段才能确定函数的调用关系,这就是动态联编。
2、动态联编又称滞后联编、晚期联编,动态联编技术能实现动态多态
3、必须将类的成员函数定义成虚函数,才可以实现动态联编

虚函数
成员函数定义为虚函数格式:

virtual<函数返回值类型><函数名>(参数列表)

例子:Li13.22

关于虚函数的内存

  • 当类中任何函数前带virtual时,编译程序将暗自在类中增加一个数据成员,在这称它为VPTR它指向一个虚函数表,因此,Withvirtual类对象占用的内存空间比Novirtual 类的对象大。
  • **每个包含了虚函数的类都包含一个虚表。**如果一个基类包含了虚函数,那么其继承类也可调用这些虚亟数,换司话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。

下面是例子和内存示意图:(了解即可)

//1
class A 
{
	public:
		virtual void vfunc1();
		virtual void vfunc2();
		void func1();
		void func2();
	private:
		int m _data1, m data2;
};

在这里插入图片描述
//2
在这里插入图片描述
在这里插入图片描述
同名的虚函数会覆盖、或称为重写,把基类的函数换掉。

虚函数总结说明:
(1)派生类的虚函数必须与基类虚函数同名,且参数的类型、个数、顺序必须一致,否则,属于函数重载,而不是虚函数。
(2)基类中虚函数前的关键字virtual不能缺省。
(3)必须通过基类对象的指针或引用调用虚函数,才能实现动态多态。
(4)虚函数必须是类的成员函数,不能是友元函数,也不能是静态成员函数。
(5)不能将构造函数定义为虚函数,但可将析构函数定义为虚函数。
(6)调用虚函数速度较。因为在运行时刻才能够临时决定调用哪一个虚函数。

虚析构函数
如果类的构造函数中有动态申请的存储空间,在析构函数中应释放该空间。
此时,建议将析构函数定义为虚函数,以便实现通过基类的指针或引用撤消派生类对象时的多态性。

纯虚函数和抽象类
Li1322.Shape即纯虚函数。

  • 在定义基类时,会遇到这样的情况:无法定义基类中虚函数的具体实现,其实现依赖于其不同的派生类。
  • 可以把基类中的虚函数定义为纯虚函数
  • 把至少包含一个纯虚函数的类称为抽象类
  • 在抽象类的派生类中,应写出纯虚函数的实现,否则,派生类依然是抽象类。
  • 抽象类只能作基类,不能定义抽象类的对象。

定义纯虚函数的一般格式:

virtual <函数返回值类型><函数名>(参数列表)=0

没有函数体。函数参数列表圆括号后面“=0”,表示将函数名的值赋予0。

关于纯虚函数和抽象类的说明

  • 抽象类只能做派生类的基类。不能定义抽象类的对象。
  • 若派生类实现了基类所有的纯虚函数,则派生类就不再是抽象类了。若派生类没有实现基类所有的纯虚函数,则派生类依然是抽象类。
  • 从一般正常使用的角度下情况下,**纯虚函数没有函数体。**从语法的角度上说,纯虚函数可以给出函数体。

第14章 输入输出流

流(stream)表示信息从源到目的端的流动,负责建立数据生产者和消费者之间的联系,数据按顺序从一个对象传送到另一对象。

I/O系统的任务就是在内存和外部设备之间稳定可靠地传输数据和解释数据。
程序中,对数据的输入/输出是以字节流实现的。
应用程序对字节序列作出各种数据解释。
C++的输入输出流是指由若干字节组成的字节序列流中的内容可以是ASCII字符、图形图像、数字音频视频等形式的信息。
在这里插入图片描述
文本流与二进制流:
数据流分为文本流和二进制流,分别对应于文本文件和二进制文件。

在文本文件中,数据是一串ASCII字符,存储的是字符的ASCII码。

二进制流中的数据是其内存映像,即数据在内存中的表示形式是什么,它在数据流中的形式就是什么。例如12345 ( int类型)在二进制文件中保存,需要占用4个字节。

缓冲区
缓冲区buffer是内存中的一个临时存储区,用来匹配不同部件数据传输率的差异。
通过使用缓冲的方式可以更高效地处理输入输出,信息从设备和程序之间传输时可临时存储在缓冲区中,等待累积成数据块或合适的事机再传送的目的地。

流类:
在这里插入图片描述

(1)标准流 提供通用输入输出操作,作为其他IO流基类系统指定的标准设备的I/O操作。
(2)文件流 以外存中的文件为对象进行输入和输出。
以文件为对象的输入输出,包括从磁盘文件输入数据,或将数据输出到磁盘文件。
(3)字符串流 对内存中指定空间进行输入和输出。
通常指定一个字符数组作为存储空间。

头文件:

iostream.h

包含操作所有输入/输出流所需的基本信息istream.h , ostream.h。

iomanip.h

包含格式化IO操纵算子,用于指定数据输入输出的格式。

fstream.h

处理文件信息,包括建立文件,读/写文件的各种操作接口。
每一种C++版本通常还包含其他一些与IO相关的库,提供特定系统的某些功能

标准输出流(课件p12-13)

cout
cerr
clog

在这里插入图片描述
标准输入流 (课件p15)
在这里插入图片描述

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

没看懂
流插入运算符<<,可用于输入基本类型数据;
流提取运算符>>,可用于输入基本类型数据,
也可重载用于输出和输入用户自定义的数据类型。

重载函数形式:

ostream & operator <<(ostream & out, T & data);//输出的运算符
//对应cout<<a; cout是第一个参数,a是第二个参数
istream & operator >> (istream & in,T & data);//输入
//同理

只能是友元函数

一个例子
在这里插入图片描述
在这里插入图片描述
用&:用系统自带的cin和cout进行输入输出。

一些函数:
*cin.getline(char p, int nCount, char delim = ‘\n’)
第一个参数是指针(数组名),第二个是输入的最大个数, 第三个是到哪个字符就停下;

char a[10];
cin.getline(a,10,':');//到:就停下,其他存入a

文件处理

三个类:

ifstream\ofstream\fstream;

C++把文件看成有序的字节流。
编码方式:文本文件/二进制文件;
存取方式:顺序读写文件/随机读写文件。

文件操作的基本步骤:

streamclass fileObj(fileName,openMode);
fileObj.open(fileName,openMode);//打开文件;
fileObj.close();//关闭文件;

打开文件方法1:
在建立对象的同时连接文件。

ifstream infile("datafile.dat",ios::in);//输入流对象
ofstream outfile("d:\\newfile.dat",ios::out);//输出流对象

方法2:
先建立流对象,再调用open函数链接外部文件。

ofstream outfile;
outfile.open(":d//newfile.dat",ios::out)

既能输入又能输出:

fstream refile("myfile.dat",ios::in|ios::out);

测试文件是否打开:

outfile.open("a.dat");
if(!outfile) cerr<<"error:unable to open file a!";

测试是否关闭成功:

a.close();
if(a.fail()) cerr<<"error to close a!";

文本文件
每个字节都是一个ASCII码。
是顺序存储文件。
默认打开方式是文本文件。

打开一个文本文件并输入数据的例子:

void main()
{
	ofstream out("text");
	if(!out) {cout<<"error!";return;}
	out<<10<<" "<<123.45<<endl;
	out<<"A short text file";
	out.close();//一定不能少!
}

这样,一个text文件就输入保存好了。

从文件读入的例子:

void main()
{
	ifstream in("test");
	if(!in){cout<<"error!";return;}
	int i;
	char ch;
	float f;
	in>>i>>ch>>f;//分别读入一个int、char、float型的数据;
	//遇到空格停止
	cout<<i<<" "<<ch<<" "<<f;//输出测试一下
	in.close();
}

格式化文件读写:

//字符的文件读写
get();
put();
//字符串:
get();
getline();

无格式文件读写
(搬字节)

istream & istream::read (char *buf,int n)
//从流中提取n个字节数据,更新对象buf

ostream &ostream :: write(char *buf,int n)

二进制文件:可保密。

打开二进制文件:

ios::binary
//默认文本文件

小结

在这里插入图片描述在这里插入图片描述

复习题

第10章:
第11章:
车与船的重量 (20 分)
计算全班学生C++课程的总成绩和平均成绩 (10 分)
计算点到直线的距离一一友元函数的应用 (20 分)
第12章:
动物世界 (15 分)
第13章:
时钟模拟 (30 分)
日期类运算符重载 (20 分)
用虚函数计算各种图形的面积 (10 分)

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值