C++通过例子理解封装、继承、多态(一)

要求

class CFileBase
{
public:
	CFileBase() {};
	virtual ~CFileBase() {};

public:
	virtual void Close() = 0;
	virtual long ReadEx(void* pBuf, long iSize) = 0;
	virtual long WriteEx(const void* pBuf, long iSize) = 0;
	virtual long Seek(long offset, int whence) = 0;
	virtual long GetLength() = 0;
	virtual BOOL Truncate(long nFileSize) = 0;	//截断文件, 不支持加长
	virtual long GetPosition() = 0;
	//virtual BOOL IsEOF() = 0;
	//virtual int  OutString(const char* fmt, ...) = 0; // 返回写入的字节数
	
public:
	long SeekToBegin() { return Seek(0, SEEK_SET); }
	long SeekToEnd() { return Seek(0, SEEK_END); }
	BOOL Read(void* pBuf, long iSize) { return ReadEx(pBuf, iSize) == iSize; }
	BOOL Write(const void* pBuf, long iSize) { return WriteEx(pBuf, iSize) == iSize; } // 要么全部写进去,要么都不写进去
	char* GetLine(char* pBuf, int iBufSize);  // 返回NULL表示文件结束
	BOOL TruncateEnd() { return Truncate(GetPosition()); }	// 从当前读写位置截断文件
};

//要求,根据上述封装的文件基类派生两种不同类型的文件操作:
//磁盘文件类和内存文件类,实现不同的接口
//内存文件由链表构成,长度可动态增加

要求使用该文件基类派生出两种文件操作类:磁盘文件和内存文件,其中内存文件由链表构成,长度可动态增加。我们先从较为简单的磁盘文件类开始。

磁盘文件类的编写

核心部分的函数都是虚函数,在派生类中定义,通过调用父类的public函数来使用这些虚函数,在子类继承后,这些虚函数被重写,在不同的子类里有不同的定义。因此,派生的磁盘文件类如下:

class CDiskFile : public CFileBase
{
public:
	CDiskFile();
	virtual ~CDiskFile();

public:
	virtual void Close();
	virtual long ReadEx(void* pBuf, long iSize);
	virtual long WriteEx(const void* pBuf, long iSize);
	virtual long Seek(long offset, int whence);
	virtual long GetLength();
	virtual long GetPosition();
	virtual BOOL Truncate(long nFileSize);	//截断文件, 不支持加长
	
public:
	BOOL Open(const char* filename);

protected:
	FILE* m_pFile;
};

 首先由于磁盘文件多了一个open的操作,因此在子类中定义一个open函数,其余函数都是虚函数。定义一个protected类型的文件指针,用于指向open的文件,同时不允许类外访问该指针。函数内对该指针的操作在类外是不可见的,这也是c++类的封装特性。

open与close函数

open函数的作用是打开指定文件,初始化文件指针。close则是关闭该文件,并反初始化文件指针。

打开文件函数原型如下:

FILE *fopen(const char *filename, const char *mode);

filename即为文件名 例如 "./test.txt"

mode为文件的打开模式,可选项如下:

mode功能
r

以只读方式打开返回FILE*指针,文件必须存在,不存在时返回NULL

r+以读写方式打开,返回文件指针,文件必须存在,否则返回NULL
rb+以 读写的方式 打开 二进制文件返回文件指针,文件必须存在,否则返回NULL
rw+

 以读写方式打开文本文件,返回文件指针,文件必须存在,否则返回NULL
每次写入时,都将文件清空,从头写。

w

以写的方式打开文件 ,文件不存在则创建文件

每次写入时,都将文件清空,从头写。

w+

以读写的方式打开文件 ,文件不存在则创建文件

每次写入时,都将文件清空,从头写。

a

追加写入方式打开文件,文件不存在则创建文件。

内容会被追加至文件末尾,不清空文件

a+

追加读写方式打开文件,文件不存在则创建文件。

内容会被追加至文件末尾,不清空文件

另外,b选项可以与其他选项任意组合,例如:ab,wb等。

关闭文件函数原型如下:

int fclose(FILE *stream)

stream:文件指针

如果关闭成功,返回0 否则返回-1

用以上两个函数写出磁盘文件类的打开/关闭文件函数

打开:

/* 
	打开文件
	返回值:
		ture:成功
		false:失败
*/
BOOL CDiskFile::Open(const char* filename)
{
	m_pFile = fopen(filename, "a+");
	if (m_pFile == NULL)
	{
		return false;
	}
	return true;
}

关闭:

/* 关闭文件 */
void CDiskFile::Close()
{
	fclose(m_pFile);
	m_pFile = NULL;
}

关闭文件函数内除了使用fclose函数,还需要将文件指针置NULL

构造函数和析构函数

 磁盘文件类的构造函数只需要初始化类中的文件指针:

CDiskFile::CDiskFile()
{
	m_pFile = NULL;
}

析构函数用于关闭文件:

CDiskFile::~CDiskFile()
{
	Close();
}

读写函数

/*
	读取文件内容
	pBuf:缓冲区指针
	iSize:读取大小,单位字节
	返回值:
			实际读取字节
*/
long CDiskFile::ReadEx(void* pBuf, long iSize)
{
	size_t irSize = 0;
	irSize = fread(pBuf, sizeof(char), iSize, m_pFile);
	return (long)irSize;
}

/*
	写入内容到文件
	pBuf:缓冲区指针
	iSize:写入大小,单位字节
	返回值
		实际写入字节
*/
long CDiskFile::WriteEx(const void* pBuf, long iSize)
{
	size_t	iwSize;
	iwSize = fwrite(pBuf, sizeof(char), iSize, m_pFile);
	return (long)iwSize;
}

用到了fread和fwrite函数原型分别如下:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
  • ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
  • size -- 这是要读取的每个元素的大小,以字节为单位。
  • nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
  • stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
  • ptr -- 这是指向要被写入的元素数组的指针。
  • size -- 这是要被写入的每个元素的大小,以字节为单位。
  • nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
  • stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。

返回值均为实际写入数据单元个数,数据单元大小由size指定,这里都指定为char,即一个字节大小。

Seek函数

/* 
	设置文件内部指针位置
	offset :偏移量
	whence:取值为 SEEK_SET  SEEK_END SEEK_CUR
	返回值:
			1 成功
			0 失败
*/
long CDiskFile::Seek(long offset, int whence)
{	
	int iflag;
	iflag = fseek(m_pFile, offset, whence);
	if (iflag != 0)
	{
		return 0;
	}

	return 1;
}

用到了fseek函数原型如下;

int fseek(FILE *stream, long int offset, int whence)
  • stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
  • offset -- 这是相对 whence 的偏移量,以字节为单位。
  • whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量含义
SEEK_SET文件的开头
SEEK_CUR文件指针当前位置
SEEK_END文件的结尾

注意,offset可以为负数,表示往前偏移

GetPosition函数

/*
	获取当前文件指针的位置
	返回值:
		从文件头到当前指针位置的偏移 单位为字节
*/
long CDiskFile::GetPosition()
{
	long iPos;
	iPos = ftell(m_pFile);
	return iPos;
}

 使用ftell函数即可获取文件指针的当前位置

long int ftell(FILE *stream)

 stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流

该函数返回位置标识符的当前值即距离文件开头多少字节。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值。 

GetLength函数

/*
	获取文件长度
	返回值:
		文件长度,单位字节
*/
long CDiskFile::GetLength()
{
	long iPreSize;
	iPreSize = GetPosition(); //记录当前指针位置 单位为字节

	long iSize;
	SeekToEnd();
	iSize = ftell(m_pFile);

	Seek(iPreSize, SEEK_SET);//恢复

	return iSize;
}

也是利用ftell函数,先记录当前指针,然后把指针移到文件最后,使用ftell即可获取文件长度。最后把指针恢复到当前位置。

截断函数

/*
	截断文件
	返回值:
		1 成功
		0 失败
*/
BOOL CDiskFile::Truncate(long nFileSize)
{
	if(!Seek(nFileSize, SEEK_SET))
	{ 
		return 0;
	}

	//const char* pszEof = EOF;
	if (!Write((char*)EOF, 1))
	{
		return 0;
	}

	return 1;
}

 截断函数非常简单,在指定位置写入一个EOF即可。

磁盘文件类的完整代码

CDiskFile.h:

#ifndef _CDISKFILE_H_
#define _CDISKFILE_H_
#include "CFileBase.h"

class CDiskFile : public CFileBase
{
public:
	CDiskFile();
	virtual ~CDiskFile();

public:
	virtual void Close();
	virtual long ReadEx(void* pBuf, long iSize);
	virtual long WriteEx(const void* pBuf, long iSize);
	virtual long Seek(long offset, int whence);
	virtual long GetLength();
	virtual long GetPosition();
	virtual BOOL Truncate(long nFileSize);	//截断文件, 不支持加长
	
public:
	BOOL Open(const char* filename);

protected:
	FILE* m_pFile;
};

#endif // _CDISKFILE_H_

CDiskFile.cpp:

/* CDiskFile.cpp : Operation of DiskFile */
#define _CRT_SECURE_NO_WARNINGS
#include "CDiskFile.h"

/* 
	打开文件
	返回值:
		ture:成功
		false:失败
*/
BOOL CDiskFile::Open(const char* filename)
{
	m_pFile = fopen(filename, "a+");
	if (m_pFile == NULL)
	{
		return false;
	}
	return true;
}

/* 关闭文件 */
void CDiskFile::Close()
{
	fclose(m_pFile);
	m_pFile = NULL;
}

/* 
	设置文件内部指针位置
	offset :偏移量
	whence:取值为 SEEK_SET  SEEK_END SEEK_CUR
	返回值:
			1 成功
			0 失败
*/
long CDiskFile::Seek(long offset, int whence)
{	
	int iflag;
	iflag = fseek(m_pFile, offset, whence);
	if (iflag != 0)
	{
		return 0;
	}

	return 1;
}

/*
	读取文件内容
	pBuf:缓冲区指针
	iSize:读取大小,单位字节
	返回值:
			实际读取字节
*/
long CDiskFile::ReadEx(void* pBuf, long iSize)
{
	size_t irSize = 0;
	irSize = fread(pBuf, sizeof(char), iSize, m_pFile);
	return (long)irSize;
}

/*
	写入内容到文件
	pBuf:缓冲区指针
	iSize:写入大小,单位字节
	返回值
		实际写入字节
*/
long CDiskFile::WriteEx(const void* pBuf, long iSize)
{
	size_t	iwSize;
	iwSize = fwrite(pBuf, sizeof(char), iSize, m_pFile);
	return (long)iwSize;
}

/*
	获取当前文件指针的位置
	返回值:
		从文件头到当前指针位置的偏移 单位为字节
*/
long CDiskFile::GetPosition()
{
	long iPos;
	iPos = ftell(m_pFile);
	return iPos;
}

/*
	获取文件长度
	返回值:
		文件长度,单位字节
*/
long CDiskFile::GetLength()
{
	long iPreSize;
	iPreSize = GetPosition(); //记录当前指针位置 单位为字节

	long iSize;
	SeekToEnd();
	iSize = ftell(m_pFile);

	Seek(iPreSize, SEEK_SET);//恢复

	return iSize;
}

/*
	截断文件
	返回值:
		1 成功
		0 失败
*/
BOOL CDiskFile::Truncate(long nFileSize)
{
	if(!Seek(nFileSize, SEEK_SET))
	{ 
		return 0;
	}

	//const char* pszEof = EOF;
	if (!Write((char*)EOF, 1))
	{
		return 0;
	}

	return 1;
}

CDiskFile::CDiskFile()
{
	m_pFile = NULL;
}

CDiskFile::~CDiskFile()
{
	Close();
}

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
封装继承和多是面向对象编程中的三大特性,C++也支持这三种特性。 1. 封装 封装是指将数据和对数据的操作封装在一起,形成一个类。类中的数据和方法可以设置为私有的,只有类内部可以访问,外部无法访问。这样可以保证数据的安全性和完整性。 下面是一个封装例子: ```c++ class Person { private: string name; int age; public: void setName(string n) { name = n; } void setAge(int a) { age = a; } string getName() { return name; } int getAge() { return age; } }; ``` 2. 继承 继承是指一个类可以继承另一个类的属性和方法。被继承的类称为父类或基类,继承的类称为子类或派生类。子类可以使用父类的属性和方法,也可以重写父类的方法。 下面是一个继承例子: ```c++ class Student : public Person { private: int id; public: void setId(int i) { id = i; } int getId() { return id; } }; ``` 3. 多是指同一种类型的对象,在不同的情况下可以有不同的表现形式。C++中实现多有两种方式:虚函数和模板。 下面是一个虚函数的例子: ```c++ class Shape { public: virtual double getArea() { return 0; } }; class Circle : public Shape { private: double radius; public: Circle(double r) { radius = r; } double getArea() { return 3.14 * radius * radius; } }; class Rectangle : public Shape { private: double width; double height; public: Rectangle(double w, double h) { width = w; height = h; } double getArea() { return width * height; } }; ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值