王宇

来自CGTWiki
跳转至:导航搜索
王宇

目录

简历

1992年1月,大专学历,毕业于湖北职业技术学院,熟悉C++技术,了解java后台技术。负责平台的测试工作。

课程列表

  1. 清华大学C++从基础到进阶(两个月)
  2. 侯捷 C++面向对象高级开发(全集)(一个月)
  3. VS2017快捷键操作(一天)
  4. SVN操作(2个小时)
  5. 线性代数(一天看完,连续五天重复看)
  6. 清华大学计算机图形学(根据兴趣看)
  7. OSG前四部分(根据兴趣看)

学习日记

10月22日 P197

  • 实验——异常处理
#include <iostream>
using namespace std;

class CException {
public:
	CException() {}
	virtual ~CException(){}
	void Reason() { cout << "CExeption" << endl; }
};
void fun1() {
	throw CException();
}
int main()
{
	try {
		fun1();
	}
	catch (CException & ce) {
		ce.Reason();
	}
	return 0;
}


10月21日 P194

  • 标准程序库异常处理
  • 标准异常类的基础
exception:标准程序库异常类的公共基类
logic_error表示可以在程序中被预先检查到的异常
	如果小心的编写程序,这类异常能够避免
runtime_error表示难以预先检测的异常
  • 例题
#include <iostream>
#include<cmath>
#include<stdexcept>
using namespace std;
//给出三角形的三边长,计算三角形的面积
double area(double a, double b, double c) throw (invalid_argument) {
	//判断三角形边长是否为正
	if (a <= 0 || b <= 0 || c <= 0) {
		throw invalid_argument("the side length should be positive");
	}
	//判断三边长是否满足三角不等式
	if (a + b <= c || b + c <= a || c + a <= b) {
		throw invalid_argument("the side length should fit the triangle inequation");
	}
	//由Heron公式计算三角形面积
	double s = (a + b + c) / 2;
	return sqrt(s*(s - a)*(s - b)*(s - c));
}
int main()
{
	double a, b, c;//三角形三边长
	cout << "Please input the side lengths of a triangle:";
	cin >> a >> b >> c;
	try {
		double s = area(a, b, c);//尝试计算三角形面积
		cout << "area:" << s << endl;
	}
	catch (exception & e) {
		cout << "Error:" << e.what() << endl;
	}
	return 0;
}


10月20日 P193

  • 异常处理中的构造与析构
  • 自动的析构
找到一个匹配的catch异常处理后
	初始化异常参数
	将从对应的try块开始到异常被抛掷外之间(且尚未析构)
		的所有自动对象进行析构
	从最后一个catch处理之后开始恢复执行
  • 例题
#include <iostream>
#include<string>
using namespace  std;
class MyException {
public:
	MyException(const string &message) :message(message) {}
	MyException(){}
	const string &getMessage() const { return message; }
private:
	string message;
};
class Demo {
public:
	Demo() { cout << "constructor of Demo" << endl; }
	~Demo() { cout << "Destructor of Demo" << endl; }
};
void func()throw(MyException) {
	Demo d;
	cout << "throw MyException in func()" << endl;
	throw MyException("exception thrown by func()");
}

int main()
{
	cout << "In main function" << endl;
	try {
		func();
	}
	catch (MyException& e) {
		cout << "Caught an exception:" << e.getMessage() << endl;
	}
	cout << "Resume the execution of main()" << endl;
	return 0;
}


10月19日 P192

  • 异常处理的思想与程序实现
  • 异常处理的语法
抛掷异常的程序段
	....
	throw  表达式;
	.....
捕获并处理异常的程序段
	try
	复合语句
	catch(异常声明)
	复合语句
	catch(异常声明)
	复合语句
	.........
若有异常则通过throw创建一个异常对象并抛掷

将可能抛出异常的程序段嵌在try块之中,通过正常的顺序执行达到
try语句,然后执行try块内的保护段

如果在保护段执行期间没有引起异常,那么跟在try块后的Catch子句
就不执行,程序从try块后的最后一个catch子句后面的语句继续执行

catch子句按其在try块后出现的顺序被检查,匹配的catch子句
将捕获并处理异常(或继续抛掷异常)

如果匹配的处理器未找到,则库函数terminate将被自动调用
其缺默认能是调用abort终止程序
  • 异常接口声明
可以在函数的声明中列出这个函数可能抛出的所有异常类型
例如:
viod fun() throw(A,B,C,D);
若无异常接口声明,则此函数可以抛掷任何类型的异常
不抛掷任何类型异常的函数声明如下
void fun() throw();
一个函数显示声明可能抛出的异常
有利于函数的调用者为异常处理做好准备
  • 例题
#include <iostream>
using namespace  std;

int divide(int x, int y) {
	if (y == 0)
		throw x;
	return x / y;
}
int main()
{
	try {
		cout << "5/2 = " << divide(5, 2) << endl;
		cout << "8/0 = " << divide(8, 0) << endl;
		cout << "7/1 = " << divide(7, 1) << endl;
	}
	catch(int e){
		cout << e << "is divided by zero!" << endl;
	}
	cout << "that is ok." << endl;
	return 0;
}


10月16-18日 P187-190

  • 输入/输出流
  • 两个重要的输入输出流
一个iostream对象可以是数据的源或目的
两个重要的I/O流类都是从iostream派生的,他们是fstream和string stream,
这些类继承了前面描述的istream和ostream类的功能
  • fstream类
fstream类支持磁盘文件输入和输出
如果需要在同一个程序中从一个特定磁盘文件读取并写到该磁盘文件
可以构造一个fstream对象
一个fstream对象是由两个逻辑子流的单个流,两个子流一个用于输出,
另一个用于输入
  • string stream类
stringstream类支持面向字符串的输入和输出
可以用对同一个字符串内容交替读写,同样是由两个逻辑子流构成
  • 输入输出综合实例
#include <fstream>
using namespace  std;
#define D(a) T << #a << endl;a
ofstream T("output.out");
int main()
{
	D(int i = 53;)
	D(float f = 4700113.141593;)

	D(T.setf(ios::unitbuf);)

		D(T.setf(ios::showbase);)
		D(T.setf(ios::uppercase);)
		D(T.setf(ios::showpos);)
		D(T << i << endl;)
		D(T.setf(ios::hex, ios::adjustfield);)
		D(T << i << endl;)
		D(T.unsetf(ios::uppercase);)
		D(T.setf(ios::oct, ios::basefield);)
		D(T << i << endl;)
		D(T.unsetf(ios::showbase);)
		D(T.setf(ios::dec, ios::basefield);)
		D(T.setf(ios::left, ios::adjustfield);)
		D(T.fill('0');)
		return 0;
}
  • 输入输出综合实例2
#include <iostream>
#include<fstream>
using namespace  std;

int main(int argc,char* argv[])
{
	ifstream in;
	in.open(argv[1], ios::binary);
	if (!in) {
		cout << "cannot open file." << endl;
		return 1;
	}
	const int bsz = 1024;
	char buf[bsz];
	int line = 0;
	while (in.getline(buf,bsz))
	{
		cout << ++line << ":" << buf << endl;
	}
	return 0;
}

10月15日 P186

  • 从字符串输入
  • 字符串输入流(istringstream)
用于从字符串读取数据
在构造函数中设置要读取的字符串
支持ifstream类的除open、close外的所有操作
典型应用:
	将字符串转换为数值
  • 例题
#include <iostream>
#include<sstream>
#include<string>
using namespace std;
template<class T>
inline T fromString(const string &str) {
	istringstream is(str);//创建字符串输入流
	T v;
	is >> v;//从字符串输入流中读取变量v
	return v;//返回变量v
}
int main()
{
	int v1 = fromString<int>("5");
	cout << v1 << endl;
	double v2 = fromString<double>("1.2");
	cout << v2 << endl;
	return 0;
}


110月14日 P184

  • 输入流概述
  • 重要的输入流类
istream类最适合用于顺序文本模式输入。cin是其实例
ifstream类支持磁盘文件输入
istringstream
  • 构造输入流对象
如果在构造函数中指定一个文件名,在构造该对象时该文件便自动打开
	ifstream  myFile(“filename”);
在调用默认构造函数之后使用open函数来打开文件
	ifstream myFile;//建立一个文件流对象
	myFile.open("filename");//打开文件“filename”
打开文件时可以指定模式
	ifstream myFile(“filename”,ios_base::in | ios_base::binary);
  • 使用提取运算符从文本文件输入
提取运算符(>>)对于所有标准C++数据类型都是预先设计好的
是从一个输入流对象获取字节最容易的方法
ios类中的很多操纵符都可以应用输入流。但是只有少数几个
对输入流对象具有实际影响,其中最重要的是进制操纵符dec,oct和hex
  • 输入流相关函数
open函数把该流与一个特定磁盘文件相关联
get函数的功能与提取运算符(>>)很相像,主要的不同点是get函数在读入数据时包括空白字符
getline的功能是从输入流中都区多个字符,并且允许指定输入终止字符,读取完后,从读取的内容中删除终止字符
read成员函数从一个文件读字节到一个指定的内存区域,由长度参数确定要读的字节数。当遇到文件结束或者在文本模式
中遇到文件结束标记字符时结束读取
  • 输出流相关函数
seekg函数用来设置文件输入流中读取数据位置的指针
tellg函数返回当前文件读取指针位置
close函数关闭与一个文件输入流关联的磁盘文件。



10月13日 P182-183

  • 向二进制文件输出
  • 二进制文件流
使用ofstream构造函数中的模式参量指定二进制输出模式
以通常方式构造一个流,然后使用setmode的成员函数,在文件打开后改变模式
通过二进制文件输出流对象完成输出
#include <iostream>
#include<fstream>
using namespace  std;
struct Date{
	int mon,day,year;
};
int main()
{
	Date dt = { 6,10,92 };
	ofstream file("date.dat", ios_base::binary);
	file.write(reinterpret_cast<char *>(&dt), sizeof(dt));
	file.close();
	return 0;
}
  • 向字符串中输出
  • 字符串输出流(ostringstream)
用于构造字符串
功能:
	支持ofstream类的除open、close外的所有操作
	str函数可以返回当前一构造的字符串
典型应用
	将数值转换为字符串
#include <iostream>
#include<sstream>
#include<string>
using namespace std;
template<class T>
inline string toString(const T &v) {
	ostringstream os;//创建字符串输出流
	os << v;		//将变量V的值写入字符串流
	return os.str();//返回输出流生成的字符串
}
int main()
{
	string str1 = toString(5);
	cout << str1 << endl;
	string str2 = toString(1.2);
	cout << str2 << endl;
	return 0;
}


10月12日 P181

  • 向文本文件输出
插入运算符(<<)
为所有标准C++数据类型预先设计的,
用于传送字节到一个输出流对象
  • 操纵符(manipulator)
插入运算符与操纵符一起工作
	控制输出格式
很多操纵符都定义在
	ios_base类中(如hex())、<iomanip>头文件(steprecision)
控制输出宽度
	在流中放入setw操纵符或调用width成员函数为每个项指定出宽度
setw和width仅影响紧随其后的输出项,但其它流格式操纵符保持
有效直到发生改变
Dec、oct和hex操纵符设置输入和输出的默认进制
  • setiosflags操纵符
这个过程中,通过使用带参数的setiosflags操纵符来设置左右对齐,
setiosflags定义在头文件iomanip中
参数ios_base::left是ios_base的静态常量,因此引用时必须包括ios_base::前缀
这里需要用resetiosflags操纵符关闭左右对齐。setiosflags不同于width和setw
他的影响是持久的,直到resetiosflags重新恢复默认值时为止
setiosflags的参数是该流的格式标志值,可用按位或(|)运算符进行组合
  • 精度
如果不指定fixed或scientific,精度值表示有效数字位数
如果设置了ios_base::fixed或ios_base::scientific精度值表示小数点之后的位数


10月11日 P179-180

  • I/O流的概念及流类库结构
当程序与外界环境进行信息交换时,存在着两个对象
一个是程序中的对象,另一个是文件对象
流是信息流动的一种抽象
它负责在数据的生产者和数据的消费者之间建立联系
并管理数据的流动
  • 流对象与文件操作
程序建立一个流对象
指定这个流对象与某个文件对象建立连接
程序操作流对象
流对象通过文件系统对所链接的文件对象产生作用
  • 提取与插入
读操作在流数据抽象中被称为(从流中)提取
写操作被称为(向流中)插入
  • 输出流概述
最重要的3哥输出流:
ostream
ofstream
ostringstream
预定义的输出流对象:
cout:标准输出
cerr:标准错误输出,没有缓冲,发送给它的类容立即被输出
clog:类似于cerr,但是有缓冲,缓冲区满时被输出
  • 标准输出换向
ofstream fout("b.out");
streambuf* pOld = cout.rdbuf(fout.rdbuf());
cout.rdbuf(pOld);
  • 构造输出流对象
ofstream 类支持磁盘文件输出
如果在构造函数中指定一个文件名,当构造这个文件时该文件是自动打开的
	ofstream myFile("filename");
可以在带哦用默认构造函数之后使用open成员函数打开文件
	ofstream  myFile;//声明一个静态文件输出流对象
	myFile。open("filename");//打开文件,使流对象与文件建立联系
在构造对象或用open打开文件时可以指定模式
	ofstream myFile("filename",ios_base::out | ios_base::binary);
  • 文件输出流成员函数的三种类型
与操作符等价的成员函数
执行非格式化写操作的成员函数
其他修改流状态且不同于操作符或插入运算符的成员函数
  • 文件输出流成员函数
open函数
	把流与一个特定的磁盘文件关联起来
	需要指定打开模式
put函数
	把一个字符写到输出流中
write函数
	将内存中的一块类容写道一个文件输出流中
seek和tellp函数
	操着文件流的内部指针
close函数
	关闭与一个文件输出流关联的磁盘文件
错误处理函数
	在写到一个流时进行错误处理
>/pre>




===10月10日      P174===
*算法
STL算法特点
<pre>
STL算法本身是一种函数模板
通过迭代器获得输入数据
通过函数对象对数据进行处理
通过迭代器将结果输出
STL算法是通用的,独立于具体的数据类型,容器类型
  • STL算法分类
不可变序列算法
可变序列算法
排序和搜索算法
数值算法
  • 不可变序列算法
不直接修改所操作的容器内容的算法
用于查找指定元素、比较两个序列是否相等、对元素进行计数等
  • 可变序列算法
可以修改它们所操作的容器对象
包括对序列进行
复制、删除、替换、倒序、旋转、交换、分割、去重、填充、洗牌的算法
及生成一个序列的算法
  • 排序和搜索算法
对序列进行排序
对两个有序序列进行合并
对有序序列进行搜素
有序序列的集合操作

10月5日 P173

  • 函数适配器
绑定适配器
组合适配器
函数指针适配器
成员函数适配器
  • 绑定适配器
bind1st、bind2nd
将n元函数对象的指定参数绑定为一个常数
得到n-1函数对象
  • 组合适配器
not1,not2
将指定谓词的结果取反
  • 函数指针适配器
ptr_fun,ptr_fun_ref
将一般函数指针转换为函数对象,使之能够作为其他函数适配器的输入
在进行参数绑定或其他转换的时候,通常需要函数对象的类型信息,
例如bind1st和bind2nd要求函数对象必须继承于binary_function类型
但如果传入的是函数指针形式的函数对象,则无法获得函数对象的类型信息
  • 例题
#include <iostream>
#include<functional>
#include<vector>
#include<algorithm>
using namespace  std;

int main()
{
	int intArr[] = { 30,90,10,40,70,50,20,80 };
	const int N = sizeof(intArr) / sizeof(int);
	vector<int> a(intArr, intArr + N);
	vector<int>::iterator p = find_if(a.begin(), a.end(), bind2nd(greater<int>(), 40));
	if (p == a.end) {
		cout << "no element greater than 40" << endl;
	}
	else
	{
		cout << "first element greater than 40 is:" << *p << endl;
	}
	return 0;
}
  • 组合适配器
对于一般逻辑运算,有时可能还需要对结果求一次逻辑反
unary_negate和binary_negate实现了这一适配功能,
STL还提供了not1和not2辅助生成相应的函数对象实例,分别用于一元谓词和二元谓词的逻辑取反
  • 例题
#include <iostream>
#include<functional>
#include<vector>
#include<algorithm>
using namespace  std;

bool g(int x, int y) {
	return x > y;
}
int main() {
	int intArr[] = { 30,90,10,40,70,50,20,80 };
	const int N = sizeof(intArr) / sizeof(int);
	vector<int> a(intArr, intArr + N);
	vector<int>::iterator p;
	p = find_if(a.begin(), a.end(), bind2nd(ptr_fun(g), 40));
	if (p == a.end())
	{
		cout << "no element greater than 40" << endl;
	}
	else
	{
		cout << "first element greater than 40 is" <<*p<< endl;
	}
	p = find_if(a.begin(), a.end(), not1(bind2nd(greater<int>(), 15)));
	if (p == a.end())
	{
		cout << "no element is not greater than 15" << endl;
	}
	else
	{
		cout << "first element that is greater than 15 is" << *p << endl;
	}p = find_if(a.begin(), a.end(), not2(bind2nd(greater<int>(), 15)));
	if (p == a.end())
	{
		cout << "no element is not greater than 15" << endl;
	}
	else
	{
		cout << "first element that is greater than 15 is" << *p << endl;
	}
	return 0;
}
  • 例题
#include <iostream>
#include<functional>
#include<vector>
#include<algorithm>
using namespace  std;

struct Car
{
	int id;
	Car(int id) { this->id = id; }
	void display() const { cout << "car" << id << endl; }
};
int main() {
	vector<Car*> pcars;
	vector<Car>cars;
	for (int i = 0; i < 5; i++)
	{
		pcars.push_back(new Car(i));
	}
	for (int  i = 5; i < 10; i++)
	{
		cars.push_back(Car(i));
	}
	cout << "element in pcars:" << endl;
	for_each(pcars.begin(), pcars.end(), std::mem_fun(&Car::display));
	cout << endl;


	cout << "element in cars:" << endl;
	for_each(cars.begin(), cars.end(), std::mem_fun_ref(&Car::display));
	cout << endl;

	for (size_t i = 0; i < pcars.size(); i++)
	{
		delete pcars[i];
	}
	return 0;
}

9月25日 P172

  • 函数对象
一个行为类似函数的对象
可以没有参数,也可以带有若干参数
其功能是获取一个值,或者改变操作的状态
例:
普通函数就是函数对象
重载了“()”运算符的类的实例是函数对象
  • 例题
#include <iostream>
//包含数值算法的头文件
#include<numeric>
using namespace std;


//定义一个普通函数
int mult(int x, int y) { return x * y; };
int main()
{
	int a[] = { 1,2,3,4,5 };
	const int N = sizeof(a) / sizeof(int);
	cout << "the result by multipling all elements in a is "
		<< accumulate(a, a + N, 1, mult) << endl;
	return 0;
}
  • 例2
#include <iostream>
//包含数值算法头文件
#include<numeric>
using namespace std;

class MultClass {
public:
	//重载操作符 operator()
	int operator()(int x, int y)const { return x * y; }
};

//主函数
int main()
{
	int a[] = { 1,2,3,4,5 };
	const int N = sizeof(a) / sizeof(int);
	cout << "the result by multipling all elements in a is "
		<< accumulate(a, a + N, 1, MultClass()) << endl;
	return 0;
}
  • 例3
#include <iostream>
//包含数值算法头文件
#include<numeric>
//包含标准函数对象头文件
#include<functional>
using namespace std;


//主函数
int main()
{
	int a[] = { 1,2,3,4,5 };
	const int N = sizeof(a) / sizeof(int);
	//将标准函数对象传递给通用算法
	cout << "the result by multipling all elements in a is "
		<< accumulate(a, a + N, 1, multiplies<int>()) << endl;
	return 0;
}

9月24日 P171

  • 多重集合和多重映射
多重集合是允许有重复元素的集合,多重映射是允许一个键对应多个附加数据的映射
多重集合与集合、多重映射与映射的用法差不多,只在几个成员函数上有细微差异
其差异主要表现在去除了键必须唯一的限制
  • 例题
#include <iostream>
#include<map>
#include<utility>
#include<string>
using namespace std;

int main()
{
	multimap<string, string> courses;
	typedef multimap<string, string>::iterator CourseIter;

	//将课程上课时间插入courses映射中
	courses.insert(make_pair("C++", "2-6"));
	courses.insert(make_pair("COMPILER", "3-1"));
	courses.insert(make_pair("COMPILER", "5-2"));
	courses.insert(make_pair("OS", "1-2"));
	courses.insert(make_pair("OS", "4-1"));
	courses.insert(make_pair("OS", "5-5"));
	//输入课程名,直到找到该课程为止,记下每周上课次数
	string name;
	int count;
	do
	{
		cin >> name;
		count = courses.count(name);
		if (count == 0)
		{
			cout << "cannot find this courses!" << endl;
		}

	} while (count == 0);
	//输出每周上课时间和上课次数
	cout << count << "lesson(s) per week: " << endl;
	pair<CourseIter, CourseIter>range = courses.equal_range(name);
	for (CourseIter iter = range.first; iter != range.second; ++iter)
	{
		cout << iter->second << " " << endl;
	}
	return 0;
}

9月23日 P170

  • 映射(map)
映射与集合同属于单重关联容器,他们的主要区别在于集合的元素类型是键本身
而映射的元素类型是由键和附加数据所构成的二元组
在集合中按照键查找一个元素时,一般只是用来确定这个元素是否存在
而在映射中按照键查找一个元素时,除了能确定他的存在性之外,还可以得到相应
的附加数据
  • 例题
#include <iostream>
#include<map>
#include<string>
#include<utility>
using namespace std;

int main()
{
	map<string, int> courses;
	//将课程信息插入courses映射中
	courses.insert(make_pair("csapp", 3));
	courses.insert(make_pair("c++", 2));
	courses.insert(make_pair("casrch", 4));
	courses.insert(make_pair("compiler", 4));
	courses.insert(make_pair("os", 5));
	//剩下可选的次数
	int n = 3;
	//学分总和
	int sum = 0;
	while (n > 0)
	{
		string name;
		//输入课程名称
		cin >> name;
		//查找课程
		map<string, int >::iterator iter = courses.find(name);
		//判断是否找到
		if (iter == courses.end())
		{
			cout << name << "is available" << endl;
		}
		else
		{
			//累加学分
			sum += iter->second;
			//将刚选过的课程从映射中删除
			courses.erase(iter);
			n--;
		}
	}
	//输出总学分
	cout << "total credit:" << sum << endl;
	return 0;
}




9月22日 P169

  • 集合(set)
集合用来存储一组无重复的元素,由于集合的元素本身是有序的
可以高效的查找指定元素,也可以方便的得到指定大小范围的元素
在容器中所处的区间
  • 例题
#include <iostream>
#include<set>
#include<iterator>
#include<utility>
using namespace std;
int main()
{
	set<double>  s;
	while (true)
	{
		double v;
		cin >> v;
		if (v == 0)
		{
			break;//输入0表示结束
		}
		//尝试将V插入
		pair <set <double>::iterator, bool> r = s.insert(v);
		if (!r.second)//如果V存在,则输出提示信息
		{
			cout << v << "is duplicated" << endl;
		}
	}
	//得到第一个元素的迭代器
	set<double>::iterator iter1 = s.begin();

	//得到末尾的迭代器
	set<double>::iterator iter2 = s.end();

	//得到最小和最大元素的中值
	double medium = (*iter1 + *(--iter2)) / 2;

	//输出小于或等于中值的元素
	cout << "<= medium:";
	copy(s.begin(), s.upper_bound(medium), ostream_iterator<double>(cout, ""));
	cout << endl;

	//输出大于或等于中值的元素
	cout << ">= medium: ";
	copy(s.lower_bound(medium), s.end(), ostream_iterator<double>(cout, ""));
	cout << endl;
	return 0;
}

9月21日 P168

  • 关联容器分类和基本功能
  • 关联容器的特点和接口
关联容器的特点
	每个关联容器都有一个键(key)
	可以根据键高效的超找元素
接口
	插入:insert
	删除:erase
	查找:find
	定界:lower_bound、upper_bound、eqal_range
	计数:count
  • 四种关联容器
单重关联容器(set和map)
	键值是唯一的,一个键值只能对应一个元素
多重关联容器(multiset和multimap)
	键值是不唯一的,一个键值可以对应多个元素
简单关联容器(set和multiset)
	容器只有一个参数类型,入set<K>,multiset<K>,表示键类型
	容器的元素就是键本身
二元关联容器
	容器有两个类型参数;map<K,V>、multimap<K,V>,分别表示键和附加数据类型
	容器的元素类型是pair<K,V>,即由键类型和元素类型复合而成的二元组
  • 无序关联容器
不是使用比较运算符类组织元素的,而是通过一个哈希函数和键类型的==运算符
提供了与有序容器相同的操作
可以直接定义关键字是内置类型的无序容器
不能直接定义关键字类型为自定义的无序容器,如果需要,必须提供我们自己的hash模板

9月20日 P167

  • 顺序容器的插入迭代器
用于向容器头部,尾部或中间指定位置插入元素的迭代器
包括前插迭代器(front_inserter)、后插迭代器(back_insrter)
和任意位置插入迭代器(inserter)
例如:
	list<int> s;
	back_inserter iter(s);
	*(iter++) = 5;//通过iter把5插入s末尾
  • 顺序容器的适配器
以顺序容器为基础构建一些常用数据结构,是对顺序容器的封装
	栈(stack):最先压入的元素最后被弹出
	队列(queue):最先压入的元素最先被弹出
	优先级队列(prioriy_queue):最“大”的元素最先被弹出
  • 栈和队列模板
栈模板:
template<class T,class Sequence = deque<T>> class stack;
队列模板
template<class T ,class FrontIntsertionSequence = deque<T>> class queue;
栈可以用任何一种顺序容器作为基础容器,而队列只允许用前插顺序容器(双端队列或列表)
  • 栈和队列不同的操作
栈
s.top()  返回栈顶元素的引用
队列操作
s.front()  获得队头元素的引用	
s.back()   获得队尾元素的引用

9月14日 P166

  • 顺序容器的特征
  • 向量(Vector)
特点
	一个可以扩展的动态数组
	随机访问、在尾部插入或删除元素快
	在中间或头部插入或删除元素慢
向量的容量
	容量(capacity):实际分配空间的大小
	s.capacity():返回当前容量
	s.reserve(n):若容量小于n则对s进行扩展,使其容量至少为n
  • 双端队列(deque)
特点:
	在两端插入或删除元素快
	在中间插入或删除元素慢
	随机访问较快,但比向量容器慢
  • 列表
特点	
	在任意位置插入和删除元素都很快
	不支持随机访问
接合(splice)操作
	s1.splice(p,s2,q1,q2):将s2中[q1,q2]移动到s1中p所指向的元素之前
  • 单向链表
单向链表每个结点只有指向下一个结点的指针,没有简单的方法来获取一个结点的前驱
未定义insert、emplace和erase操作,而定义了insert_after、
	emplace和erase相同,但并不是插入或删除迭代器p1所指的元素,而是对p1所指元素之后的结点进行操作
不支持size操作
  • 数组
array是对内置数组的封装,提供了更安全,更方便的使用数组的方式
array的对象的大小是固定的,定义时除了需要指定元素类型,还需要指定容器大小
不能动态的改变容器大小



9月13日 P165

  • 顺序容器的基本功能
STL中的顺序容器
	向量(vector)
	双端队列(deque)
	列表(list)
	单项链表(forward_list)
	数组(array)
元素线性排列,可以随时在指定位置插入元素和删除元素
必须符合Assignable这一概念(具有公有的复制构造函数并可以用‘=’赋值)
array对象的大小固定,forward_list有特殊的添加和删除操作
  • 顺序容器的接口(不包含单向链表和数组)
构造函数
赋值函数
	assign
插入函数
	insert,clear,pop_front(只对list和deque),push_back,  emplace,  emplace_front
删除函数
	erase,clear,pop_front(只对list和deque),pop_back,  emplace_back
首尾元素的直接访问
	front,back
改变大小
	resize
  • 顺序容器
#include <iostream>
#include<list>
#include<deque>
using namespace std;
//输出指定的顺序元素
template<class T>
void printContainer(const char* msg, const T& s) {
	cout << msg << " : ";
	copy(s.begin(), s.end(), ostream_iterator<int>(cout," "));
	cout << endl;
}

template<class T>
int main()
{
	//从标准输入对数10个整数,从s的头部加入
	deque<int> s;
	for (int i = 0; i < 10; i++)
	{
		int x;
		cin >> x;
		s.push_front(x);
	}
	printContainer("deque at first", s);
	//用s容器的内容的可逆构造列表容器l
	list<int> l(s.rbegin(), s.rend());
	printContainer("list at frist", l);
	//将列表容器l的每相邻两个元素顺序颠倒
	list<int>::iterator iter = l.begin();
	while (iter != l.end()) {
		int v = *iter;
		iter = l.erase(iter);
		l.insert(++iter, v);
	}
	printContainer("list at last", l);
	s.assign(l.begin(), l.end());
	printContainer("deque at last", s);
	return  0;
}

9月12日 P164

  • 容器的基本功能与分类
容器时容纳、包含一组元素或元素集合的对象
基于容器中元素的组织方式分类:
顺序容器、关联容器
按照与容器所关联的迭代器类型分类:
可逆容器、随机访问容器
  • 容器
顺序容器
	array(数组)、vector(向量)、depue(双端队列)、forward_list(单链表)、list(列表)
(有序)关联容器
	set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)
无序关联容器
	unordered_set(无序集合)、unordered_multiset(无序多重集合)
	unordered_map(无序映射)、unorder_multimap(无序多重映射)
  • 容器的通用功能
用默认构造函数构造空容器
	支持关系运算符:==,!=,<,<=,>,>=
	begin()、end():获取容器首尾迭代器
	clear():将容器清空
	empty():判断容器是否为空
	size():得到容器元素个数
	s1.swap(s2):将s1和s2两容器类容交换
相关数据类型
	S::iterator:指向容器元素的迭代器类型
	S::const_iterator:常迭代器类型
  • 对可逆容器的访问
STL为每个可逆容器都提供了逆向迭代器,逆逆向迭代器可以通过下面成员函数得到
	rbegin():指向容器尾的逆向迭代器
	rend():指向容器首的逆向迭代器
逆向迭代器的类型名的表示方法如下:
	S::reverse_iterator:	逆向迭代器类型
	S::const_reverse_iterator:逆向常迭代器类型
  • 随机访问容器
支持对容器的元素进行随机访问
s[n]:获得容器s的第n个元素

9月11日 P163

  • 迭代器
迭代器是算法和容器的桥梁
	迭代器作用访问容器中的元素
	算法不直接操作容器中的数据,而是通过迭代器间接操作
算法和容器独立
	增加新的算法,无需影响容器的实现
	增加新的容器,原有的算法也能适用
  • 输入流迭代器和输出流迭代器
输入流迭代器
	istream_iterator<T>
	以输入流(如cin)为参数构造
	可以用*(p++)获得下一个输入的元素
输出流迭代器
	ostream_iterator<T>
	构造时需要提供输出流(如cout)
	可用*(p++) = x 将x输出到输出流
二者都属于适配器
	适配器是用来为已有对象提供新的接口的对象
	输出流适配器和输入流适配器为流对象提供了迭代器的接口


9月10日 P162

  • STL的基本组件
容器(container)
迭代器(iterator)
函数对象(function   object)
算法(algorithms)
  • iterator(迭代器)是算法和容器的桥梁

将迭代器作为算法的参数,通过迭代器来访问容器 而不是把容器直接作为算法的参数

  • 将函数对象作为算法的参数而不是将函数所执行的运算作为算法的一部分
  • 使用STL中提供的或自定义的迭代器和函数对象,配合STL的算法,可以组合出

各种各样的功能

  • STL--容器
容纳、包含一组元素的对象
基本容器类模板
	顺序容器
	array(数组)、vector(向量)、depue(双端队列)、forward_list(单链表)、list(列表)
	(有序)关联容器
	set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)
	无序关联容器
	unordered_set(无序集合)、unordered_multiset(无序多重集合)
	unordered_map(无序映射)、unorder_multimap(无序多重映射)
  • 容器适配器
stack(栈)
queue(队列)
prioroty_queue(优先队列)
  • 使用容器,需要包含对应的头文件
  • STL——迭代器(iterator)
	 提供了顺序访问容器中每个元素的方法
	可以使用“++”运算符来获得指向下一个元素的迭代器
	可以使用“*”运算符访问迭代器所指向的元素,如果元素类型是类或结构体
还可以用“->”运算符直接访问该元素的一个成员
	有些迭代器还支持通过“--”运算符获得指向上一个元素的迭代器
	迭代器是泛化的指针:指针也具有同样的特性,因此指针本身就是一种迭代器
	使用独立于STL容器的迭代器,需要包含头文件<iterator>
  • 函数对象
一个行为类似函数的对象,对它可以像调用函数一样调用
函数对象是泛化的函数:任何普通的函数和任何重载了“()”运算符的类的对象都可以作为函数对象使用
使用STL的函数对象,需要包含头文件<functional>
  • 算法
可以广泛用于不同的对象和内置的数据类型
使用STL的算法,需要包含头文件<algorithm>
  • STL程序实例
#include <iostream>
#include<vector>
#include<iterator>
#include<algorithm>
#include<functional>
using namespace std;

int main()
{
	const int N = 5;
	vector<int> s(N);
	for (int  i = 0; i < N; i++)
	{
		cin >> s[i];
	}
	transform(s.begin(), s.end(), ostream_iterator<int>(cout, " "), negate<int>());
	cout << endl;
	return 0;	
}

  • transform算法
transform算法的一种实现
#include <iostream>
#include<vector>
#include<iterator>
#include<algorithm>
#include<functional>
using namespace std;

int main()
{
	const int N = 5;
	vector<int> s(N);
	for (int  i = 0; i < N; i++)
	{
		cin >> s[i];
	}
	transform(s.begin(), s.end(), ostream_iterator<int>(cout, " "), negate<int>());
	cout << endl;
	return 0;	
}
transform算法顺序遍历first和last两个迭代器所指向的元素;
将每个元素的作为函数对象op的参数
将op的返回值通过迭代器result顺序输出
遍历完成后result迭代器指向的时输出的最后一个元素的下一个位置
	transform将会该迭代器返回


9月9日 P161

  • 泛型程序设计
编写不依赖于具体数据类型的程序
将算法从特定的数据结构中抽象出来,成为通用的
  • 术语:概念
用来界定具备一定功能的数据类型
例如:
	将“可以比大小的所有数据类型”这一概念记为comparable
	将“具有公有的复制构造函数并可以用‘=’赋值的数据类型“ 这一概念记为assignable
	将“可以比大小、具有公有的复制构造函数并可以用‘=’赋值的所有数据类型”
这个概念记为sortable
对于两个不同的概念A和B,如果概念A所需求的所有功能也是概念B所需求的功能,
那么就说概念B是概念A的子概念
例如
	sortable既是comparable的子概念也是assignable的子概念
  • 术语:模型
模型(model):符合一个概念的数据类型称为概念的模型
例如:
	int 型是comparable概念的模型
	静态数组类型不是assignable概念的模型
	(无法用“=”给整个静态数组赋值)
  • 用概念做模板参数名
为概念赋予一个名称,并使用该名称作为模板参数名
表示insertionSort这样一个函数模板原型:
	template<class T>
	void insertionSort(Sortable a[],int n );


9月8日 P156

  • 查找
  • 顺序查找
从顺序查找的首元素开始,逐个元素与待查找的关键字进行比较,
知道找到相等的。
若整个序列中没有与待查找关键字相等的元素,就是查找不成功
template <class T>
int seepSearch(const T list[], int n, const T &key) {
	for (int i = 0; i < n; i++)
	{
		if (list[i] == key)
		{
			return i;
		}
	}
	return -1;
}
<source >

*折半查找(二分法查找)
<pre>
对于已按关键字排序的序列,经过一次比较,可将序列分割成两部分
然后只在有可能包含待查找元素的一部分继续查找,并根据试探结果
继续分割,逐步缩小查找范围,直至找到或找不到为止
</pre>

<source lang="cpp">
template <class T>
int seepSearch(const T list[], int n, const T &key) {
	int low = 0;
	int high = n - 1;
	while (low <= high) {
		int mid = (low + high) / 2;
		if (key < list[mid])
			return mid;
		else 
			if (key < list[mid])
				high = mid - 1;
		else
			low = mid + 1;

	}
	return -1;
}


9月7日 P155

  • 交换排序
  • 交换排序的基本思想
两两比较待排序序列中的元素,
并交换不满足顺序要求的各队元素
直到全部满足顺序要求为止
  • 起泡排序
template<class T>
void mySwap(T &x, T&y) {
	T temp = x;
	x = y;
	y = temp;
}
template<class T>
void bubbleSort(T a[], int n) {
	int i = n - 1;
	while (i>0)
	{
		int lastExchangeIndex = 0;
		for (int  j = 0; j < i; j++)
		{
			if (a[j+1]<a[j])
			{
				mySwap(a[j], a[j + 1]);
				lastExchangeIndex = j;
			}
			i = lastExchangeIndex;
		}
	}
}

9月6日 P154

  • 选择排序
  • 选择排序的基本思想
每次从待排序序列中选择一个关键字最小的元素
顺序排在已排序序列的最后,直至全部排完
  • 选择排序示意

template<class T>
void mySwap(T &x, T&y) {
	T temp = x;
	x = y;
	y = temp;
}
template <class T>
void selectionSort(T a[], int n) {
	for (int  i = 0; i < n-1; i++)
	{
		int leastIndex = i;
		for (int j = i+1; j < n; j++)
		{
			if (a[j]<a[leastIndex])
			{
				leastIndex = j;
			}
			mySwap(a[j], a[leastIndex]);
		}
	}
}

9月4日 P152-P153

  • 排序概述
排序:将数据元素的任意序列,重新排列成一个按关键字偶虚的序列
*数据元素:数据的基本单位,在计算机中常作为一个整体进行考虑
	一个数据元素可以由若干数据项组成
关键字:数据元素中某个数据项的值,用它可以标识一个数据元素
在排序过程中需要完成两种基本操作:
	比较两个数的大小;
	调整元素在序列中的位置;
  • 内部排序外部排序
  • 内部排序
待排序的数据元素存放在
计算机内存中进行的排序过程
  • 外部排序
排序的数据元素数量很大,
以致内存中一次不能容纳全部数据
在排序过程中尚需对外存进行访问的排序过程
  • 插入排序
  • 插入排序的基本思想
每一步将一个待排序元素按其关键字值的大小插入到已排序
序列的适当位置上,直到待排序元素插入完为止
template <class T>
void insertionSort(T a[],int n){
	int i , j;
	T temp;
	for(int i = 1;i < n;i++){
		int j = i;
		T temp = a[i];
		while( j > 0 && temp < a[j - 1] ){
			a[j] = a[j - 1];
			j--;
		}
	a[j] = temp;
	}
}

9月3日 P151

  • 队列类模板

<pew> 队列是只能向一端添加元素,从另一端删除元素的线性群体。 是一种先进先出的结构

  • 队列的基本状态
队空:队列中没有元素
队满:队列中元素个数达到上限
一般状态:队列中有元素,但未达到队满状态
  • 循环队列
在想象中将数组弯曲呈环形,元素出队时,后继元素不移动,
每当队尾达到数组最后一个元素时,便再回到数组开头
#ifndef QUEUE_H
#define QUEUE_H
#include<cassert>
template <class T,int SIZE = 50>
class Queue {
private:
	int front, rear, count;  //队头、队尾指针,元素个数
	T list[SIZE];//队列元素数组
public:
	Queue();//构造函数,初始化队头、队尾指针,元素个数
	void insert(const T &item);//新元素入队
	T remove();//元素出队
	void clear();//清空队列
	const T &getFront() const;//访问队首元素
	int getLength() const;//求队列长度
	bool isEmpty() const;//判断队列状态是否为空
	bool isFull() const;//判断队列满否
};
template<class T, int SIZE>
Queue<T, SIZE>::Queue():front(0),rear(0),count(0){}
template<class T, int SIZE>
void Queue<T, SIZE>::insert(const T & item)
{
	assert(const != SIZE);
	count++;
	list[rear] = item;
	rear = (rear + 1) % SIZE;
}
template<class T, int SIZE>
T Queue<T, SIZE>::remove()
{
	assert(const != 0);
	int temp = front;
	count--;
	front = (front + 1) % SIZE;
	return list[temp];
}
template<class T, int SIZE>
void Queue<T, SIZE>::clear()
{
	count = 0;
	front = 0;
	rear = 0;
}
template<class T, int SIZE>
const T & Queue<T, SIZE>::getFront() const
{
	return list[front];
}
template<class T, int SIZE>
int Queue<T, SIZE>::getLength() const
{
	return count;
}
template<class T, int SIZE>
bool Queue<T, SIZE>::isEmpty() const
{
	return count == 0;
}
template<class T, int SIZE>
bool Queue<T, SIZE>::isFull() const
{
	return count == SIZE;
}
#endif // !QUEUE_H


8月29-30日 P150

  • 栈的应用
  • Stack.h
#ifndef STACK_H
#define STACK_H
#include<cassert>
template <class T,int SIZE = 50>
class Stack {
private:
	T list[SIZE];
	int top;
public:
	Stack();
	void push(const T &item);
	T pop();
	void clear();
	const T &peek() const;
	bool isEmpty() const;
	bool isFull() const;
};
//模板的实现
template <class T ,int SIZE>
Stack<T ,SIZE>::Stack():top(-1){}
template <class T ,int SIZE>
void Stack<T, SIZE>::push(const T &item) {
	assert(!isFull());
	list[++top] = item;
}
template <class T ,int SIZE>
T Stack<T, SIZE>::pop() {
	assert(isEmpty());
	return list[top--];
}
template <class T ,int SIZE>
const T &Stack<T, SIZE>::peek() const {
	assert(!isEmpty());
	return list[top];//返回栈顶元素
}
template<class T, int SIZE>
bool Stack<T, SIZE>::isEmpty() const{
	return top == -1;
}
template<class T, int SIZE>
bool Stack<T, SIZE>::isFull() const{
	return top == SIZE - 1;
}
template<class T, int SIZE>
void Stack<T, SIZE>::clear() {
	top = -1;
}
#endif //  !
  • Claculator.h
#ifndef CLACULATOR_H
#define CLACULATOR_H
#include "Stack.h"
class Claculator {//计算器类
private:
	Stack<double> s;//操作栈数
	void enter(double num);//num压入栈
	bool getTwoOperands(double &opnd1, double &opnd2);//连续操作2次弹出栈
	void computer(char op);//执行操作符op指定的运算
public:
	void run();//运行计算器程序
	void clear();//清空操作数栈
};
#endif // 

  • Claculator.cpp
#include "Claculator.h"
#include <iostream>
#include<sstream>
#include<cmath>
using namespace std;
//工具函数   用于将字符串转换为实数
 double stringToDouble(const string &str) {
	 istringstream stream(str);
	 double result;
	 stream >> result;
	 return result;
}
 //将操作数num 压入栈
 void Claculator::enter(double num) {
	 s.push(num);
}

 bool Claculator::getTwoOperands(double & opnd1, double & opnd2)
 {//检查栈是否空
	 if (s.isEmpty())
	 {
		 cerr << "Missing operand!" << endl;
		 return false;
	 }
	 opnd1 = s.pop();//将右操作数弹出栈
	 if (s.isEmpty())//检查栈是否空
	 {
		 cerr << "missing operand!" << endl;
		 return false;
     }
	 opnd2 = s.pop();//将左操作数弹出栈
	 return true;
	 
 }

 void Claculator::computer(char op)
 {
	 double operand1, operand2;
	 bool result = getTwoOperands(operand1, operand2);
	 if (result) {
		 switch (op)
		 {
			case'+':s.push(operand2 + operand1);
				break;
			case'-':s.push(operand2 - operand1);
				break;
			case'*':s.push(operand2 * operand1);
				break;
			case'/':if (operand1 == 0) 
			{
				cerr << "divided by 0!" << endl;
				s.clear();//阶数为0时清空栈
			}
			else
			{
				s.push(operand2 / operand1);
			}
					break;
		 }
		 cout << " = " << s.peek() << endl;//输出本次运算结果
	 }
	 else
	 {
		 s.clear();//操作数不够,清空栈
	 }
 }

 void Claculator::run()
 {
	 string str;
	 while (cin >> str,str != "q")
	 {
		 switch (str[0])
		 {
		 case 'c':s.clear();
			 break;
		 case '-':if (str.size() > 1) 
			{
			 enter(stringToDouble(str));
			}
			else {
			 computer(str[0]);
			}
				  break;
		 case '+':
		 case '^':
		 case '*':
		 case '/':
			 computer(str[0]);
		 default:
			 enter(stringToDouble(str));
			 break;
		 }
	 }
 }

 void Claculator::clear()
 {
	 s.clear();
 }

  • main
#include <iostream>
#include"Claculator.h"
int main()
{
	Claculator c;
	c.run();
	return 0;
}

8月28日 P149

  • 栈类模板
  • 栈是只能从一端访问线性群体,是一种后进先出的数据结构
  • 栈的基本状态
栈空:栈中没有元素
栈满:栈中元素个数达到上限
一般状态:栈中有元素,但未达到栈满状态
  • 栈的基本操作
初始化
入栈
出栈
清空栈
访问栈顶元素
检测栈的状态(满   空)
  • 例题
#ifndef STACK_H
#define STACK_H
#include<cassert>
template <class T,int SIZE = 50>
class Stack {
private:
	T list[SIZE];
	int top;
public:
	Stack();
	void push(const T &item);
	T pop();
	void clear();
	const T &peek() const;
	bool isEmpty() const;
	bool isFull() const;
};
//模板的实现
template <class T int SIZE>
Stack<T ,SIZE>::Stack():top(-1){}
template <class T int SIZE>
void Stack<T, SIZE>::push(const T &item) {
	assert(!isFull());
	list[++top] = item;
}
template <class T int SIZE>
T Stack<T, SIZE>::pop() {
	assert(isEmpty());
	return list[top--];
}
template <class T int SIZE>
const T &Stack<T, SIZE>::peek() const {
	assert(!isEmpty());
	return list[top];//返回栈顶元素
}
template<class T, int SIZE>
bool Stack<T, SIZE>::isEmpty() const{
	return top == -1;
}
template<class T, int SIZE>
bool Stack<T, SIZE>::isFull() const{
	return top == SIZE - 1;
}
template<class T, int SIZE>
void Stack<T, SIZE>::clear() {
	top = -1;
}
#endif //  !

8月27日 P148

  • 链表类模板
  • 链表的基本操作
生成链表
插入链表
查找结点
删除结点
遍历链表
清空链表
#ifndef NODE_H
#define NODE_H
template<class T>
class Node {
private:
	Node<T> *next;//指向后继结点的指针
public:
	T data;//数据域
	Node(const T &data, Node<T> *next = 0);//构造函数
	void insertAfter(Node<T> *P);//在本结点之后插入一个同类结点P
	Node<T> *deleteAfter();//删除本节点的后继节点,并返回其地址
	Node<T> *nextNode();//获取后继节点的地址
	const Node<T> *nextNode() const;//获取后继节点的地址
};
template<class T>
Node<T>::Node(const T&data, Node<T>*next = 0) :data(data), next(next) {}
template<class T>
void Node<T>::insertAfter(Node<T>* P)
{
	p->next = next;
	next = p;
}
template<class T>
Node<T>* Node<T>::deleteAfter()
{
	Node<T> *tempPttr = next;
	if (next == 0) {
		return 0;
	}
	next = tempPtr->next;
	return tempPtr;
}
template<class T>
Node<T> *Node<T>::nextNode() {
	return next;
}
template<class T>
const Node<T> *Node<T>::nextNode() const {
	return next;
}
#endif // !NODE_H
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include"Node.h"
template <class T>
class LinkedList {
private:
	Node<T> *front, *rear;//表头和表尾指针
	Node<T> *prvePtr, *currPtr;//记录当前遍历的指针,更新操作
	int size;//表中的元素个数
	int position;//当前元素在表中的序号,由函数reset使用
	//生成性的结点,数据域为etem,指针域为ptrNext
	Node<T> *newNode(const T&item, Node<T> *ptrNext = NULL);
	void freeNode(Node<T> *p);//释放结点
	//将链表L复制到当前表,被复制构造函数、operator = 调用
	void copy(const LinkedLish<T> & L);
public:
	LinkedList();//构造函数
	LinkedList(const LinkedList<T> &L);//复制构造函数
	~LinkedList();//析构函数
	//重载赋值运算符
	LinkedList<T> &operator = (const LinkedList<T> &L);

	int getSize() const;//返回链表中元素个数
	bool isEmpty() const;//链表是否为空
	
	void reset(int pos = 0);//初始化游标位置
	void next();//使游标移动到下一个结点
	bool endOfList() const;//游标是否到了链尾
	int currentPosition() const;//返回游标当前的位置

	void insertFront(const T &item);//在表头插入结点
	void insertRead(const T &item);//在表尾添加结点
	void insertAt(const T &item);//在当前结点之前插入结点
	void insertAfter(const T &item);//在当前结点之后插入结点

	T deleteFront();//删除头结点
	void deleteCurrent();//删除当前结点

	T &data();//返回对当前结点成员数据的引用
	const T& data() const;//返回对当前结点成员数据的常引用

	//清空链表  释放所有结点的内存空间,被析构函数、operator = 调用
	void clear();
};
#endif // !1
#include <iostream>
#include"LinkedList.h"
using namespace std;
int main()
{
	LinkedList<int> list;
	for (int i = 0; i < 10; i++)
	{
		int item;
		cin >> item;
		list.insertFront(item);
	}
	cout << "List: ";
	list.reset();
	while (!list.endOfList()) {
		cout << list.data() << " ";
		list.next();
	}
	cout << endl;
	int key;
	cout << "please enter some integer needed to be delete:";
	cin >> key;
	list.reset();
	while (!list.endOfList)
	{
		if (list.data() == key)
		{
			list.deleteCurrent();
		}
		list.next();
	}
	cout << "List: ";
	while (!list.endOfList)
	{
		cout << list.data() << " ";
		list.next();
	}
	cout << endl;
	return 0;
}



8月26日 P147

  • 链表的概念与接点类模板
  • 顺序访问的线性群体——链表类
链表是一种动态数据结构,可以用来表示顺序访问的线性群体
链表是由系列结点组成的,结点可以在运行时动态生成
每个结点包括数据域和指向链表中下一个结点的指针(即下一个结点的地址)
  如果链表每个结点中只有一个指向后继结点的指针,则该链表称为单链表
  • 结点类模板
#ifndef NODE_H
#define NODE_H
template<class T>
class Node {
private:
	Node<T> *next;//指向后继结点的指针
public:
	T data;//数据域
	Node(const T &data, Node<T> *next = 0);//构造函数
	void insertAfter(Node<T> *P);//在本结点之后插入一个同类结点P
	Node<T> *deleteAfter();//删除本节点的后继节点,并返回其地址
	Node<T> *nextNode();//获取后继节点的地址
	const Node<T> *nextNode() const;//获取后继节点的地址
};
template<class T>
Node<T>::Node(const T&data,Node<T>*next = 0):data(data),next(next){}
template<class T>
 void Node<T>::insertAfter(Node<T>* P)
{
	p->next = next;
	next = p;
}
template<class T>
 Node<T>* Node<T>::deleteAfter()
{
	 Node<T> *tempPttr = next;
	 if (next == 0) {
		 return 0;
	 }
	 next = tempPtr->next;
	 return tempPtr;
}
template<class T>
Node<T> *Node<T>::nextNode() {
	return next;
}
template<class T>
const Node<T> *Node<T>::nextNode() const {
	return next;
}
#endif // !NODE_H


8月25日 P145

  • 数组类模板
  • 直接访问的线性群体——数组类
静态数组是具有固定元素个数的群体,其中的元素可以通过下标直接访问
缺点:大小在编译时就已经确定,在运行时无法修改
动态数组由一系列位置连续的、任意数量相同类型的元素组成
优点:某元素个数可在程序运行时改变
  • 例题
#ifndef ARRAY_H
#define ARRAY_H
#include<cassert>

template<class T>//数组类模板定义
class Array {
private:
	T * list;//用于存放动态分配的数组内存首地址
	int size;//数组大小(元素个数)
public:
	Array(int sz = 50); //构造函数
	Array(const Array<T>&a);//复制构造函数
	~Array();//析构函数
	Array<T>&operator = (const Array<T>&rhs);
	T&operator[](int i);
	const T&operator[](int i)const;
	operator T*();
	operator const T * () const;
	int getSize() const;
	void resize(int sz);
};
template<class T>
Array<T>::Array(int sz) {
	assert(sz >= 0);
	size = sz;
	list = new T[size];
}
template<class T>
 Array<T>::Array(const Array<T>& a)
{
	 size = a.size;
	 list = new T[size];
	 for (int i = 0; i < size; i++)
	 {
		 list[i] = a.last[i];
	 }
}
 template<class T>
 Array<T>::~Array() {
	 delete[] list;
 }
 template<class T>
  Array<T>& Array<T>::operator=(const Array<T>& rhs)
 {
	  if (&rhs != this)
	  {
		  if (size != rhs.size) {
			  delete[] list;
			  size = rhs.size;
			  list = new T[size];
		  }
		  for (int  i = 0; i < size; i++)
		  {
			  list[i] = rhs.list[i];
		  }
	  }
	  return *this;
 }

template <class T>
T&Array<T>::operator[](int n) {
	assert(n >= 0 && n < size);
	return list[n];
}
template<class T>
const T &Array<T>::operator[](int n)const {
	assert(n >= 0 && n < size);
	return list[n];
}
template<class T>
 Array<T>::operator T*()
{
	return list;
}
template<class T>
Array<T>::operator const T*() const
{
}
template<class T>
int Array<T>::getSize() const {
	return size;
}
template<class T>
void Array<T>::resize(int sz)
{
	assert(sz >= 0);
	if (sz == size) {
		return;
	}
	T*newList = new T[sz];
	int n = (sz < size) ? sz : size;
	for (int i = 0; i < n; i++)
	{
		newList[i] = list[i];
	}
	delete[] list;
	list = newList;
	size = sz;
}
#endif //
#include <iostream>
#include"array.h"
using namespace std;

void read(int *p, int n) {
	for (int i = 0; i < n; i++)
	{
		cin >> p[i];
	}
}
int main()
{
	Array<int> a(10);
	read(a, 10); 
	return 0;
}


8月24日 P144

  • 线性群体的概念
群体是指由多个数组元素组成的集合
群体可以分为两大类:
	线性群体和非线性群体
线性群体中的元素按位置排列有序,
	可以区分为第一个元素,第二个元素
非线性群体不用位置顺序来标识元素
  • 线性群体
线性群体中元素次序与其逻辑位置关系是对应的
	在线性群体中又可以按照访问元素的不同方法
          分为直接访问、顺序访问和索引访问


测试

运行时会有两个警告   并无错误
正常运行时快速操作   没有报错
已知没有改正的几个错误还是存在
1,路径漫游中的“开始”点击后会报错
2,路径漫游中的“结束”点击后会报错
3,主界面显示的“海拔”均为负数
建议:
       经纬度可以加上“E,S,W,N”

8月23日 P143

  • 类模板
作用:
使用类模板使用户可以为类声明一种模式
使得类中的某些数据成员、、
某些成员函数的参数、
某些成员函数的返回值
能取任意类型
(包括基本类型和用户自定义类型)
  • 类模板的声明
*类模板
	template<模板参数表>
	class类名
	(类成员声明)
如果需要在类模板以外定义成员函数,则要采用以下形式:
	template<模板参数表>
	类型名 类名<模板参数标识符列表>::函数名(参数表)
  • 例题
#include <iostream>
#include<cstdlib>
using namespace std;
struct Student {
	int id;		//学号
	float gpa; //平均分
};
template <class T>
class Store {//类模板:实现对任意数进行存取
private:
	T item;//用于存放任意类型数据
	bool havaValue;//标记item是否已被存入数据
public:
	Store();
	T & getElem();//提取数据函数
	void putElem(const T & x);//存入数据函数
};
template <class T>
Store<T>::Store():havaValue(false){}
template <class T>
T&Store<T>::getElem() {
	if (!havaValue) {
		cout << "NO item present" << endl;
		exit(1);
	}
	return item;
}
template <class T>
void Store<T>::putElem(const T&x) {
	havaValue = true;
	item = x;
}
int main()
{
	Store<int> s1, s2;
	s1.putElem(3);
	s2.putElem(-7);
	cout << s1.getElem() << " " << s2.getElem() << endl;

	Student g = { 1000,23 };
	Store<Student> s3;
	s3.putElem(g);
	cout << "the student id is " << s3.getElem().id << endl;

	Store<double> d;
	cout << "retrieving object D...";
	cout << d.getElem() << endl;
	return 0;
}

8月22日 P142

  • 函数模板
语法定义:
	template<模板参数表>
	函数定义
模板参数表的类容
	类型参数:class(或typename)标识符
	常量参数:类型说明符 标识符
	模板参数:template<参数表>class 标识符
  • 例题
#include <iostream>
using namespace std;
//定义函数模板
template<typename T>
T abs(T x) {
	return x < 0 ? -x : x;
}
int main()
{
	int n = 5;
	double d = 5.5;
	cout << abs(n) << endl;
	cout << abs(d) << endl;
	return 0;
}
  • 注意
一份函数模板并非自动可以处理所有类型的数据
只有能够进行函数模板中运算的类型,可以作为类型实参
自定义的类,需要重载模板中的运算符,才能作为类型实参

8月21日 P140

  • 实例
#include <iostream>
using namespace std;
class Point {
	int _x, _y;
public:
	Point(int x = 0, int y = 0) :_x(x), _y(y){}
	//前置和后置的自增自减
	Point & operator++();
	Point operator++(int);
	Point & operator--();
	Point operator--(int);
	friend ostream & operator<<(ostream & o, Point & p);
};
Point& Point::operator++() {
	_x++;
	_y++;
	//返回引用
	return *this;
}
Point Point::operator++(int) {
	Point temp = *this;
	++ *this;
	//返回对象本身
	return temp;
}
Point& Point::operator--() {
	_x--;
	_y--;
	//返回引用
	return *this;
}
Point Point::operator--(int) {
	Point temp = *this;
	-- *this;
	//返回对象本身
	return temp;
}
ostream & operator<<(ostream & o, Point & p) {
	o << '(' << p._x << ',' << p._y << ')';
	return o;
}
int main()
{
	Point p(1, 2);
	cout << p << endl;
	cout << ++p << endl;
	cout << --p << endl;
	return 0;
}

8月20日 P137

  • override月final
  • override
多态行为的基础:
	基类声明虚函数,派生类声明一个函数,覆盖虚函数
覆盖要求:
	函数签名(signature)完全一致
函数签名包括:
	函数名  参数列表 const
  • 例题
#include <iostream>
using namespace std;
class Base {
public:
	virtual void f1(int)const;
	virtual~Base() {

	};
};
void Base::f1(int)const {
	cout << "base f1" << endl;
	return;
}
class Derived :public Base {
public:
	void f1(int);
	~Derived() {

	};
};
void Derived::f1(int) {
	cout << "derived f1" << endl;
}

int main()
{
	Base *b;
	b = new Base;
	b->f1(1);
	b = new Derived;
	b->f1(1);
	return 0;
}


8月19日 P136

  • 抽象类
  • 纯虚函数
纯虚函数是一个在基类中声明的虚函数,它在该基类中没有定义具体的操作类容
要求各派生类根据实际需要定义自己的版本
纯虚函数的声明格式为:
	virtual 函数类型 函数名(参数表)  =  0;
  • 抽象类的语法
带头纯虚函数的类称为抽象类
class 类名{
	virtual 类型 函数名 (参数表) = 0;
	//其他成员。。。。。。。
}
  • 抽象类作用
将有关的数据和行为组织在一个继承层次结构中
	保证派生类具有要求的行为
对于暂时无法实现的函数,可以声明为纯虚数
	留给派生类去实现
  • 注意
抽象类只能作为基类来使用
不能定义抽象类的对象
#include <iostream>
using namespace std;
class Base1 {
public:
	virtual void display()const = 0;//纯虚函数
};
void Base1::display() const {
	cout << "Base1::display()" << endl;
}
class Base2 :public Base1 {
public:
	virtual void display() const;//覆盖基类的虚函数
};
void Base2::display()const {
	cout << "Base2::display()" << endl;
}
class Dervied :public Base2 {
public:
	virtual void display()const;//覆盖基类的虚函数
};
void Dervied::display() const {
	cout << "Derived::display()" << endl;
}
void fun(Base1 *ptr) {
	ptr->display();
}
int main() {
	Base2 base2;
	Dervied dervide;
	fun(&base2);
	fun(&dervide);
	return 0;
}

8月18日 P135

  • 虚表与动态绑定
  • 虚表
每个多态类有一个虚表(virtual table)
虚表中有当前类的各个虚数的入口地址
每个对象有一个指向当前类的虚表的指针(虚指针)
  • 动态绑定的实现
构造函数中为对象的虚指针赋值
通过多态类型的指针或引用调用成员函数时
	通过虚指针找到虚表,进而找到所调用的虚函数入口地址
通过该入口地址调用虚函数

8月17日 P134

  • 虚析构函数
允许其他人通过基类指针调用对象的析构函数
就需要让基类的析构函数成为虚函数
否则执行delete的结果是不正确的
  • 派生类的析构函数都不是虚函数
  • 例题
#include <iostream>
using namespace std;
class Base {
public:
	virtual ~Base();
};
Base::~Base() {
	cout << "Base destructor" << endl;
}
class Derived:public Base {
public:
	Derived();
	virtual ~Derived();
private:
	int *p;
};
Derived::Derived() {
	p = new int(0);
}
Derived::~Derived() {
	cout << "derived destructor" << endl;
	delete p;
}
void fun(Base* b) {
	delete b;//静态绑定,只会调用~base
}
int main()
{
	Base* b = new  Derived();
	fun(b);
	return 0;
}

8月16日 P133

  • 虚函数
用virtaul关键字说明的函数
虚函数是实现运行时多态性基础
C++中的虚函数是动态绑定的函数
虚函数必须是非静态的成员函数
虚函数经过派生之后,就可以实现运行过程中的多态
  • 什么函数可以是虚函数
一般成员函数可以是虚函数
构造函数不能是虚构函数
析构函数可以是虚函数
  • 一般虚函数成员
虚函数的声明
	virtual 函数类型 函数名(形参表)
虚函数声明只能出现在类定义中的函数原型声明中
	而不能再成员函数实现的时候
在派生类中可以对基类中的成员函数进行覆盖
虚函数一般不声明为内联函数
	因为对虚函数的调用需要动态绑定
	而内联函数的处理是静态的
  • 例题
#include <iostream>
using namespace std;
class Base1 {
public:
	virtual void display()const;
};
void Base1::display() const {
	cout << "Base1::display()" << endl;
}
class Base2:public Base1 {
public:
	virtual void display() const;
};
void Base2::display()const {
	cout << "Base2::display()" << endl;
}
class Dervied :public Base2 {
public:
	virtual void display()const;
};
void Dervied::display() const {
	cout << "Derived::display()" << endl;
}
void fun(Base1 *ptr) {
	ptr->display();
}
int main() {
	Base1 base1;
	Base2 base2;
	Dervied dervide;
	fun(&base1);
	fun(&base2);
	fun(&dervide);
	return 0;
}

8月15日 P132

  • 运算符重载为非成员函数
  • 规则
函数的形参代表依自左至右次序排列的各操作数。
重载为非函数成员时
	参数个数 = 原操作数个数(后置++,--除外)
	至少应该有一个自定义类型的参数
后置单目运算符++和--的重载函数,形参列表中药增加一个int,但不必写形参名
如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此类函数声明为该类的友元。

双目运算符 B重载后,
	表达式oprd1 B oprd2
	等同于operator B(oprd1,oprd2)
前置单目运算符 B重载后
	表达式B oprd
	等同于operator B(oprd)
后置单目运算符 ++和--重载后
	表达式 oprd B
	等同于 operator B(oprd,0)
  • 例题
#include <iostream>
using namespace std;
class Complex {
public:
	Complex(double r = 0.0, double i = 0.0) :real(r),image(i){ }
	friend Complex operator+(const Complex &c1, const Complex&c2);
	friend Complex operator-(const Complex &c1, const Complex&c2);
	friend ostream & operator<<(ostream &out, const Complex&c);

private:
	double real, image;
};
Complex operator+(const Complex &c1, const Complex&c2) {
	return Complex(c1.real + c2.real, c1.image + c2.image);
}
Complex operator-(const Complex &c1, const Complex&c2) {
	return Complex(c1.real - c2.real, c1.image - c2.image);
}
ostream & operator<<(ostream &out, const Complex&c) {
	out << "(" << c.real << "," << c.image << ")";
	return out;
}
int main()
{
	Complex c1(5, 4), c2(2, 10), c3;
	cout << "c1 = " << c1 << endl;
	cout << "c2 = " << c2 << endl;
	c3 = c1 - c2;
	cout << "c3 = c1-c2" << c3 << endl;
	c3 = c1 + c2;
	cout << "c3 = c1+c2" << c3 << endl;
	
}

8月14日 P131

  • 单目运算符重载为函数成员
  • 前置单目运算符重载规则
如果要重载U为类成员函数,使之能够实现表达式U oprd
其中oprd为A类对象,则U应被重载为A类的成员函数,无形参
经重载后
	表达式U oprd相当于oprd.operator U()   
  • 后置单目运算符 ++或--重载规则
如果要重载++或--为类成员函数,使之能够实现表达式oprd--或oprd++,其中oprd为A类对象
则++或--应被重载为A类的成员函数,且具有一个int类形参。
经重载后
	表达式oprd++相当于oprd.operator++(0)
  • 前置和后置例题
  • 前置Clock & operator++();
  • 后置Clock operator++(int );
#include <iostream>
using namespace std;
class Clock {//定义时钟类
public :
	Clock(int hour = 0, int minute = 0, int second = 0);
	void showTime() const;

	//前置单目运算符重载
	Clock & operator ++ ();
	//后置单目运算符重载
	Clock  operator ++ (int);
private:
	int hour, minute, second;
};
Clock::Clock(int hour, int minute, int second) {
	if (0 <= hour && hour < 24 && 0 <= minute && minute < 60 && 0 <= second & second < 60) {
		this->hour = hour;
		this->minute = minute;
		this->second = second;
	}
	else
	{
		cout << "Time error!" << endl;
	}
}
void Clock::showTime() const {
	cout << hour << " : " << minute << " : " << second << endl;
}
Clock & Clock::operator ++ () {
	second ++;
	if (second >= 60)
	{
		second -= 60;
		minute ++;
		if (minute >= 60)
		{
			minute -= 60;
			hour = (hour + 1) % 24;
		}
	}
	return *this;
}
    Clock  Clock::operator ++ (int) {
	//注意形参表中的整形参数
	Clock old = *this;
	//调用前置“++”运算符
	++(*this);//当前对象临时的值
	return old;
}
int main()
{
	Clock myClock(23, 59, 59);
	cout << "fiest time output: ";
	myClock.showTime();
	cout << "show myClock ++: ";
	(myClock++).showTime();
	cout << "show ++myClock: ";
	(++myClock).showTime();
	return 0;
}

8月13日 P130

  • 双目运算符重载为成员函数
重载为类成员的运算符函数定义形式
	函数类型   operator 运算符(形参)
	{
		.........
	}
参数个数=原操作个数-1(后置++、--除外)
  • 双目运算符重载规则
如果要重载B为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中
oprd1为A类对象,则B应被重载为A类的成员函数,形参类型应该是
oprd2所属类型
经重载后,表达式oprd1 B oprd2 相当于oprd1.op二胺投入B(oprd2)
#include <iostream>
using namespace std;
class Complex {
public:
	Complex(double r = 0.0,double i = 0.0):real(r),imag(i){ }
	//运算符+重载成员函数
	Complex operator + (const Complex & c2)const;
	//运算符-重载成员函数
	Complex operator - (const Complex & c2)const;
	void display() const;//输出复数
private:
	double real;//复数实部
	double imag;//复数虚部
};
Complex Complex::operator + (const Complex & c2)const {
	//创建一个临时无名对象作为返回值
	return Complex(real + c2.real, imag + c2.imag);
}
Complex Complex::operator-(const Complex & c2)const {
	//创建一个临时无名对象作为返回值
	return Complex(real - c2.real, imag - c2.imag);
}
void Complex::display() const {
	cout << "(" << real << "," << imag << ")" << endl;
}
int main()
{
	Complex c1(5, 4), c2(2, 10), c3;
	cout << "c1 = ";
	c1.display();
	cout << "c2 = ";
	c2.display();
	//使用重载运算符完成复数减法
	c3 = c1 - c2;
	cout << "c3  = c1 - c2 = ";
	c3.display();
	//使用重载运算符完成复数加法
	c3 = c1 + c2;
	cout << "c3 = c1 + c2 = ";
	c3.display();
	return 0;
}


8月12日 P129

  • 运算符重载的规则
C++几乎可以重载全部的运算符,而且只能够重载C++中已有的
	不能重载的运算符:	
		“.”、“.*”、“::”,“?:”
重载之后运算符的优先级和结合性都不会改变

运算符重载时针对新类型数据的实际需要,对原有运算符进行适当的改造
例如:
	使复数类的对象可以用“+”运算符实现加法
	使时钟类对象可以用“++”运算符实现时间增加一秒
  • 重载为类的费静态成员函数;
  • 重载为非成员函数


8月7日 P123

  • 访问从基类继承的成员
  • 当派生类与基类中有相同成员是
若未特别限定,则通过派生类对象使用的是派生类中的同名成员
如果要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定
#include <iostream>
using namespace std;

class Base1 {
public:
	int var;
	void fun() { cout << "member of base1" << endl; }
};
class Base2 {
public:
	int var;
	void fun() { cout << "member of base2" << endl; }
};

class Derived : public Base1, public Base2 {//定义派生类derived
public :
	int var;//同名数据成员
	void fun() { cout << "member of derived" << endl; }//同名函数成员
};

int main()
{
	Derived d;
	Derived *p = &d;

	d.var = 1;//对象名成员标识
	d.fun();//访问D1成员

	d.Base1::var = 2;//作用域分辨标识符
	d.Base1::fun();//访问Base1基类成员

	p -> Base2::var = 3;//作用域分辨标识符
	p -> Base2::fun();//访问Base2基类成员
	return 0;
}

  • 二义性问题
如果从不同的基类继承了同名成员,但是在派生类中没有定义同名成员
	“派生类对象名或引用名.成员名”、“派生类指针 ->成员名”,访问成员存在二义性问题
解决方式:用类名限定。



8月6日 P122

  • 派生类的析构函数
析构函数不被继承,派生类如果需要,要自行声明析构函数
声明方法与无继承关系时类的析构函数相同
不需要显式的调用基类的析构函数,系统会自动隐式调用
限制性拍啊盛磊析构函数的函数体,在调用基类的析构函数
#include<iostream>
using namespace std;
class Base1 {//基类Base1,构造函数有参数
public:
	Base1(int i) { cout << "constructing Base1 *" << i << endl; }
	~Base1() { cout << "destructing Base1" << endl; }
};
class Base2 {//基类Base2,构造函数有参数
public:
	Base2(int j) { cout << "constructing Base2 *" << j << endl; }
	~Base2() { cout << "destructing Base2" << endl; }
};
class Base3 {//基类Base3,构造函数无参数
public:
	Base3() { cout << "Constructing Base3 * " << endl; }
	~Base3() { cout << "destructing Base3" << endl; }
};
class Derived :public Base2, public Base1, public Base3 {
	//派生新类Derived,注意基类名的顺序
public:
	//派生类的共有成员
	Derived(int a, int b, int c, int d) :Base1(a), member2(d), member1(c), Base2(b) {}
	//注意基类名的个数于顺序,注意成员对象名的个数与顺序
private://派生类的私有成员
	Base1 member1;
	Base2 member2;
	Base3 member3;
};
int main() { //主函数
	Derived obj(1, 2, 3, 4);
	return 0;
}


8月5日 P121

  • 派生类的复制构造函数
  • 若派生类类有声明复制构造函数
编译器会在需要时生成一个隐含的复制构造函数
县调用基类的复制构造函数
在为派生类新增的成员执行复制
  • 若派生类定义复制构造函数
一般都要为基类的复制构造函数传递参数
复制构造函数只能接受一个参数,既用来初始化派生类定义的成员
	也将被传递给基类的复制构造函数
基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用

8月4日 P120

  • 派生类构造函数举例
#include<iostream>
using namespace std;
class Base1 {//基类Base1,构造函数有参数
public:
	Base1(int i) { cout << "constructing Base1" << i << endl; }
};
class Base2  {//基类Base2,构造函数有参数
public:
	Base2(int j) { cout << "constructing Base2" << j << endl; }
};
class Base3 {//基类Base3,构造函数无参数
public:
	Base3() { cout << "Constructing Base3" << endl; }
};
class Derived :public Base2,public Base1,public Base3 {
	//派生新类Derived,注意基类名的顺序
public:
	//派生类的共有成员
	Derived(int a,int b,int c,int d):Base1(a),member2(d),member1(c),Base2(b){}
	//注意基类名的个数于顺序,注意成员对象名的个数与顺序
private ://派生类的私有成员
	Base1 member1;
	Base2 member2;
	Base3 member3;
};
int main() { //主函数
	Derived obj(1, 2, 3, 4);
	return 0;
}


8月3日 P119

  • 派生类的构造函数
默认情况下
	基类的构造函数不被继承
	派生类需要定义自己的构造函数
C++11规定
	可以用using 语句来继承基类构造函数
	但是只能初始化从基类继承的成员
	语法形式:using B::B;
  • 若不继承基类的构造函数
派生类新增成员:派生类定义构造函数初始化
继承来的成员:自动调用基类构造函数进行初始化
派生类的构造函数需要给基类的构造函数传递参数
  • 单继承时构造函数的定义语法
派生类名::派生类名(基类所需的形参 , 本类成员所需的形参):
	基类名(参数表),本类成员初始化列表
{
	//其他初始化
}
#include <iosteam>
using namespace std;
class B{
public:
	B();
	B(int i);
	~B();
	void print() const;
private:
	int b;
};
B::B(){
	b = 0;
	cout<<"B is default constructor called. "<< endl;
}
B::B(int i){
	b = i;
	cout <<"B is consturctor called."<< endl;
}
B::~B(){
	cout <<"B is destructor called."<< endl;
}
void B::print() const {
	cout << b << endl;
} 
class C:public B{
public:
	c();
	C(int i, int j);
	~C();
	void print() const ;
private:
	int c;
};
C:C(){
	c = 0;
	cout <<"C is default constructor called." <<end;
}
C:C(int i,int j) : B( i ), c( i ){
	cout <<"C is constructor called."<< endl;
}
C:~C(){
	cout <<"C is destructor calles." <<endl;
}
vois C:print() const {
	B:print();
	cout << c << endl;
}
int main (){
	C obj( 5 , 6 );
	obj.print();
	return 0;
}
  • 多重继承时构造函数的定义语法
派生类::派生类名(参数表)
基类名1(基类1初始化参数表)
基类名2(基类2初始化参数表)
.....
基类名n(基类n初始化参数表)
本类成员初始化列表
{
	//其他初始化
};
  • 派生类与基类的构造函数
当基类有默认的构造函数时
	派生类构造函数可以不向基类构造函数传递参数
	构造派生类的对象时,基类博人的构造函数将被调用
如需执行基类中带参数的构造函数
	派生类构造函数应为基类构造函数提供参数
  • 构造函数的执行顺序
1. 调用基类构造函数
	顺序按照他们被继承时声明的顺序(从左向右)
2. 对初始化列表中的成员进行初始化
	按照他们在类中定义的顺序
	对象成员初始化时自动调用其所属类的构造函数
		由初始化列表提供参数
3. 执行派生类的构造函数体中的内容



8月2日 P118

  • 类型转换
公有派生类对象可以被当做基类的对象使用,反之则不可
	派生类的对象可以隐含转换为基类对象;
	派生类的对象可以初始化基类的引用
	派生类的指针可以隐含转换为基类的指针
通过几类对象、指针只能使用从基类继承的成员
  • 转换规则
#include<iostream>
using namespace std;
class Base1 {//基类Base1定义
public:
	void display() const {
		cout<<"Base1::display()"<<endl;
	}	
};
class Base2:public Base1{//公有派生类Base2定义
public:
	void display() const {
		cout<<"Base2::display()"<<endl;
	}
};
class Derived:public Base2 {//公有派生类Derived定义
public:
	void displat() construction{
		cout<<"Derived::display()"<<endl;
	}		
};
void fun(Base1 *ptr) {  //参数为指向基类对象的指针
	ptr ->display();     //“对象指针 ->成员名”
}
int main() { //主函数
	Base1 base1;//声明base1类对象
	Base2 basr2;//声明base2类对象
	Derived derived;//声明derived类对象

	fun(&base1);//用Base1对象的指针调用fun函数
	fun(&base2);//用Base2对象的指针调用fun函数
	fun(&derived);//用Derived 对象的指针调用fun函数
`
	return 0 ;
}

  • 不要重新定义继承而来的非虚函数

8月1日 P117

  • 私有继承和保护继承
  • 私有继承(private)
继承的访问控制
	基类的public和protected成员:都以private身份出现在派生类中;
	基类的private成员:不可直接访问
访问权限
	派生类中的成员函数:可以直接访问基类中public和protected成员,但是不能直接访问基类的private成员;
通过派生类的对象:不能直接访问从基类继承的任何成员
//point
#ifndef _POINT_H
#define _POINT_H
class Point {//基类:point类的定义
public://公有成员
	void intiPoint(float x = 0, float y = 0) { this->x = x, this->y = y; }
	void move(float offX, float offY) { x += offX, y += offY; }
	float getX() const{ return x; }
	float getY() const { return y; }
private://私有成员
	float x, y;
};
#endif // !_POINT_H
//Rectamgle
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include"Point.h"

class Rectangle :private Point {//派生类定义部分
public ://新增公有函数成员
	void intiRectangle(float x, float y,float w,float h) {
		intiPoint(x, y);
		this->w = w;
		this->h = h;
	}
	void move(float offX,float offY) { Point::move(offX,offY); }
	float getX() const { return Point::getX(); }
	float getY() const { return Point::getY(); }
	float getH() const {return h; }
	float getW() const {return w; }
private://新增私有数据成员
	float w, h;
};
//116.cpp
#include <iostream>
#include"Rectangle.h"
#include"Point.h"
using namespace std;
int main()
{
	Rectangle rect;//定义类的对象
	rect.intiRectangle(2, 3, 20, 10);//设置矩形数据
	rect.move(3, 2);//移动矩形数据
	//输出矩形的特征参数
	cout << "the date of rect(x,y,w,h): " << endl;
	cout << rect.getX() << " , "
		<< rect.getY() << " , "
		<< rect.getW() << " , "
		<< rect.getH() << endl;
	return 0;
}
  • 保护继承
继承的访问控制
	基类的public和protected成员:都以protected身份出现在派生类中;
	基类的private成员:不可直接访问
访问权限
	派生类中的成员函数:可以直接访问基类中public和protected成员,但是不能直接访问基类的private成员;
通过派生类的对象:不能直接访问从基类继承的任何成员
  • protected成员的特点预作用
对建立其所在类对象的模块来说,他与private成员的性质相同
对于其派生类来说,它与public成员的性质相同
即实现了数据影藏,又方便继承,实现代码重用
  • 对错例题
class A{
protected:
	int x;
};
int main(){
	A a;
	ax = 5;//错误
}

class A{
protected:
	int x;
};
class B{
public:
	coid function();
};
void B::function(){
	x = 5; //正确
}
  • 多继承
class A{
public:
	void setA(int);
	void showA() const;
private:
	int a;
};
class B{
public:
	void setB(int);
	void showB() const;
private:
	int b;
};
class c:public A, private B{
public:
	void setC(int ,int ,int);
	void showC() const;
private const;
	int c;
};
void A::setA(int x){
	a = x;
}
void B::setB(int x){
	b = x;
}
void C::setC(int x,int y,int z){
//派生类成员直接访问基类的公有成员
	setA(x);
	setB(Y);
	c = z;
}

int  main(){
	C obj;
	obj.setA(5);
	obj.show();
	obj.setC(4,7,9);
	obj.showC();
	//obj.setB(6);错误
	//obj.showB();错误
	return 0;

7月31日 P116

  • 继承方式及公有继承
  • 不同继承方式的影响主要体现在
派生类成员对基类成员的访问权限
通过派生类对象对基类成员的访问权限
  • 三种继承方式:公有继承,私有继承,保护继承
  • 公有继承(public)
继承的访问控制
	基类的public和protected成员:访问属性在派生类中保持不变;
	基类的private成员:不可直接访问
访问权限
	派生类中的成员函数:可以直接访问基类中public和protected成员,但是不能直接访问基类的private成员;
通过派生类的对象:只能访问public成员
//point
#ifndef _POINT_H
#define _POINT_H
class Point {//基类:point类的定义
public://公有成员
	void intiPoint(float x = 0, float y = 0) { this->x = x, this->y = y; }
	void move(float offX, float offY) { x += offX, y += offY; }
	float getX() const{ return x; }
	float getY() const { return y; }
private://私有成员
	float x, y;
};
#endif // !_POINT_H
//Rectamgle
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include"Point.h"

class Rectangle :public Point {//派生类定义部分
public ://新增公有函数成员
	void intiRectangle(float x, float y,float w,float h) {
		intiPoint(x, y);
		this->w = w;
		this->h = h;
	}
	float getH()const { return h; }
	float getW()const { return w; }
private://新增私有数据成员
	float w, h;
};
#endif // !1
//116.cpp
#include <iostream>
#include"Rectangle.h"
#include"Point.h"
using namespace std;
int main()
{
	Rectangle rect;//定义类的对象
	rect.intiRectangle(2, 3, 20, 10);//设置矩形数据
	rect.move(3, 2);//移动矩形数据
	//输出矩形的特征参数
	cout << "the date of rect(x,y,w,h): " << endl;
	cout << rect.getX() << " , "
		<< rect.getY() << " , "
		<< rect.getW() << " , "
		<< rect.getH() << endl;
	return 0;
}
  • 跳过112-113

7月30日 P115

  • 继承的基本概念
  • 继承与派生概述
继承与派生是同一过程从不同角度看
	保持已有类的特性而构造新类的过程称为继承
	在已有类的基础上新增自己的特性而产生新类的过程称为派生
被继承的已有类称为基类(或父类)
派生出的新类称为派生类(或子类)
直接参与派生出某类的基类称为直接基类
基类的基类甚至更高层的基类长为间接基类
  • 继承的目的
实现设计与代码的重用
  • 派生的目的
当新的问题出现,原有程序无法解决(或不能完全解决)时,
需要对原有程序进行改造
  • 单继承时派生类的定义
语法:
class 派生类名:继承方式    基类名
{
成员声明
}
例:
class Derived:public Base
{
public :
	Derived();
	~derived();
};
  • 多继承时派生类的定义
语法
class 派生类名:继承方式1 基类名,继承方式2  基类名2,....
{
成员声明
} 
注意:
每一个“继承方式”,只用于限制对紧随其后之基类的继承
例
class Derived:public BASE1,private Base2
{
public :
	Derived();
	~Derived();
};
  • 派生类的构成
吸收基类成员
改造基类成员
添加新的成员
  • 吸收基类成员
默认情况下派生类包含了全部基类中除构造和析构函数外的所有成员
规定可以用using语句继承基类构造函数
  • 改造基类成员
如果派生类声明了一个和基类成员同名的新成员,派生的新成员就隐藏或覆盖了外层的同名成员
  • 添加新类成员
派生类增加新成员使派生类在功能上有所发展

7月29日 P110

  • C风格字符串
  • 字符串常亮
例 “pogram”
名字符连续,顺序存放,每个字符占一个字节,以“\0”结尾
相当于一个隐含创建的字符常量数组
“program”出现在表达式中,表示这一char数组的的首地址
首地址可以赋值给char常量 指针
const char *STRINGI =  “program”;
  • 用字符数组存储字符串
例如:
char str	[8] = {'p','r','o','g','r','a','m','\0'};
char str [8] = "program";
char str[] = "program";

用字符串数组表示字符串的缺点

执行连接、拷贝、比较等操作,都需要显式调用库函数,很麻烦
当字符串长度不确定时,需要用new动态创建字符串,最后要用delete释放,很繁琐
字符串实际长度大于为他分配的空间,会产生数组下标越界的错误

7月28日 P109

  • 移动构造
c++标准中提供了一种心的构造方法——移动构造
c++之前,乳沟要将源对象的状态转移到目标对象只能通过复制
在某些情况下,我们没有必要复制对象——只要移动他们
C++11引入移动语句:
	源对象的控制权全部交给目标对象
移动构造函数
	当对象在被复制后,就不再被利用了。我们完全可以吧临时
对象的资源直接移动,就这样避免了多余的复制操作
  • 在有可被利用的临时对象时触发移动构造
  • 移动构造函数
class_name(class_name &&)
  • 例题
#include <iostream>
using namespace std;
class IntNum {//构造函数
public:
	IntNum(int x = 0) :xptr(new int(x)) {
		cout << "calling constructor..." << endl;
	}
	IntNum(const IntNum &n) :xptr(new int(*n.xptr)) {//复制构造函数
		cout << "callint copy constructor..." << endl;
	}
	IntNum(IntNum&&n) :xptr(n.xptr) {//移动构造函数
		n.xptr = nullptr;
		cout << "calling move constructor..." << endl;
	}
	~IntNum() {//析构函数
		delete xptr;
		cout << "destructing..." << endl;
	}
	int getInt() { return *xptr; }
private:
	int *xptr;
};
IntNum getNum() {
	IntNum a;
	return a;
}
int main()
{
	cout << getNum().getInt() << endl;
	return 0;
}

7月27日 P108续

  • 深层复制
#include <iostream>
#include<cassert>
using namespace std;


class Point {//类的声明
public:
	Point() :x(0), y(0) {
		cout << "default construuctor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "constructor called." << endl;
	}
	~Point()
	{
		cout << "destructor called." << endl;
	}
	int getX()const { return x; }
	int getY()const { return y; }
	void move(int newX, int newY) {
		x = newX;
		y = newY;
	}
private:
	int x, y;
};
class ArrayfoPoints {//动态数组
public:
	ArrayfoPoints(const ArrayfoPoints & pointsArray);
	ArrayfoPoints(int size) :size(size) {
		points = new Point[size];
	}
	~ArrayfoPoints() {
		cout << "deleteing...." << endl;
		delete[] points;
	}
	Point&element(int index) {
		assert(index >= 0 && index < size);
		return points[index];
	}
private:
	Point *points;//指向动态数组首地址
	int size;//数组大小
};
ArrayfoPoints::ArrayfoPoints(const ArrayfoPoints & v) {
	size = v.size;
	points = new Point[size];
	for (int i = 0; i < size; i++)
	{
		points[i] = v.points[i];
	}
}
int main()
{
	int count;
	cout << "please enter the count fo points: ";
	cin >> count;
	ArrayfoPoints pointsArray1(count);//创建数组对象
	pointsArray1.element(0).move(5, 0);//访问数组元素的成员
	pointsArray1.element(1).move(15, 20);////访问数组元素的成员

	ArrayfoPoints pointsArray2(pointsArray1);//创建副本

	cout << "Copy of pointsArray1:" << endl;
	cout << "point_0 of array2:" << pointsArray2.element(0).getX() << ","
		<< pointsArray2.element(0).getY() << endl;
	cout << "point_0 of array2:" << pointsArray2.element(1).getX() << ","
		<< pointsArray2.element(1).getY() << endl;

	pointsArray1.element(0).move(25., 30);
	pointsArray1.element(1).move(35, 40);

	cout << "after the moving of pointsArray1:" << endl;
	cout << "Copy of pointsArray1:" << endl;
	cout << "point_0 of array2:" << pointsArray2.element(0).getX() << ","
		<< pointsArray2.element(0).getY() << endl;
	cout << "point_0 of array2:" << pointsArray2.element(1).getX() << ","
		<< pointsArray2.element(1).getY() << endl;

	return 0;

}

7月26日 P108

  • 深层复制与浅层复制
  • 浅层复制
实现对象间数据元素的一一对应复制
  • 深层复制
当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制
#include <iostream>
#include<cassert>
using namespace std;


class Point {//类的声明
public:
	Point() :x(0), y(0) {
		cout << "default construuctor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "constructor called." << endl;
	}
	~Point()
	{
		cout << "destructor called." << endl;
	}
	int getX()const { return x; }
	int getY()const { return y; }
	void move(int newX, int newY) {
		x = newX;
		y = newY;
	}
private:
	int x, y;
};
class ArrayfoPoints {//动态数组
public:
	ArrayfoPoints(int size) :size(size) {
		points = new Point[size];
	}
	~ArrayfoPoints() {
		cout << "deleteing...." << endl;
		delete[] points;
	}
	Point&element(int index) {
		assert(index >= 0 && index < size);
		return points[index];
	}
private:
	Point *points;//指向动态数组首地址
	int size;//数组大小
};
int main()
{
	int count;
	cout << "please enter the count fo points: ";
	cin >> count;
	ArrayfoPoints pointsArray1(count);//创建数组对象
	pointsArray1.element(0).move(5, 0);//访问数组元素的成员
	pointsArray1.element(1).move(15, 20);////访问数组元素的成员

	ArrayfoPoints pointsArray2(pointsArray1);//创建副本
	
	cout << "Copy of pointsArray1:" << endl;
	cout << "point_0 of array2:" << pointsArray2.element(0).getX() << ","
		<< pointsArray2.element(0).getY() << endl;
	cout << "point_0 of array2:" << pointsArray2.element(1).getX() << ","
		<< pointsArray2.element(1).getY() << endl;

	pointsArray1.element(0).move(25.,30);
	pointsArray1.element(1).move(35, 40);

	cout << "after the moving of pointsArray1:" << endl;
	cout << "Copy of pointsArray1:" << endl;
	cout << "point_0 of array2:" << pointsArray2.element(0).getX() << ","
		<< pointsArray2.element(0).getY() << endl;
	cout << "point_0 of array2:" << pointsArray2.element(1).getX() << ","
		<< pointsArray2.element(1).getY() << endl;
	
	
	return 0;

7月25日 P107

  • vector对象
  • 为什么要用vector?
封装任何类型的动态数组,自动创建和删除
数组下表越界检查
  • vector对象的定义
vector<元素类型>数组对象名(数组长度);
例:
vector<int> arr(5);
建立大小为5的int数组
  • vector对象的使用
对数组元素的引用
	与普通数组具有相同的形式
	vector对象名[下标表达式]
	vector数组对象名不表示数组首地址
获得数组长度
	用size函数
	vector对象名.size()
  • 应用举例
# include <iostream>
# include <vector>
using namespace std;

//计算机数组arr中元素的平均值
double average(const vector<double> %arr)
{
	double sum = 0;
	for(unsigned 8 = 0;i < arr.size();i++)
		sum += arr[i];
	return sum/arr.size();
}

int main(){
	unsigned n;
	cout <<"n = ";
	cin>>n;
	
	vector <double>arr(n);//创建数组对象
	fout <<"please input "<<n<<"rel numbers:"<<endl;
	for(unsigned i = 0;i<n;i++)
		cin>>arr[i];
	cout<<"average = "<<average(arr)<<endl;
	return 0; 
}
  • for循环配合auto
# include <iostream>
# include <vector>
int main(){
std::vector<int> v = {1,2,3};
for(auto i = v.begin();i != v.end;++i)
	std::cout<<*i<<std::endl;
for(auto e:v)
	std::cout <<e<<std::endl;
}

7月24日 P106

  • 智能指针

C++的智能指针

unique_ptr:
不允许多个指针共享资源,可以用标准库中的move函数转移指针
shared_ptr:
多个指针共享资源
weak_ptr:
可以复制shared_ptr,但其构造或者释放对资源不产生影响

7月23日 P105

  • 将动态数组封装成类
更加简洁,便于管理
可以在访问数组元素前检查下标是否越界
#include <iostream>
#include<cassert>
using namespace std;


class Point {//类的声明
public:
	Point() :x(0), y(0) {
		cout << "default construuctor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "constructor called." << endl;
	}
	~Point()
	{
		cout << "destructor called." << endl;
	}
	int getX()const { return x; }
	int getY()const { return y; }
	void move(int newX, int newY) {
		x = newX;
		y = newY;
	}
private:
	int x, y;
};
class ArrayfoPoints {//动态数组
public:
	ArrayfoPoints(int size) :size(size) {
		points = new Point[size];
	}
	~ArrayfoPoints() {
		cout << "deleteing...." << endl;
		delete[] points;
	}
	Point&element(int index) {
		assert(index >= 0 && index < size);
		return points[index];
	}
private:
	Point *points;//指向动态数组首地址
	int size;//数组大小
};
int main()
{
	int count;
	cout << "please enter the count fo points: ";
	cin >> count;
	ArrayfoPoints points(count);//创建数组对象
	points.element(0).move(5, 0);//访问数组元素的成员
	points.element(1).move(15, 20);////访问数组元素的成员


	return 0;
}


7月22日 P104

  • 申请和释放动态数组
  • 分配和释放动态数组
分配:new 类型名T{数组长度}
数组长度可以是任何整数类型表达式,在运行时计算
释放:delete[]数组名P
释放指针P所指向的数组
P必须是用new分配得到的数组首地址
#include <iostream>
using namespace std;

class Point {//类的声明
public:
	Point() :x(0), y(0) {
		cout << "default construuctor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "constructor called." << endl;
	}
	~Point()
	{
		cout << "destructor called." << endl;
	}
	int getX()const { return x; }
	int getY()const { return y; }
	void move(int newX, int newY) {
		x = newX;
		y = newY;
	}
private:
	int x, y;
};
int main()
{
	Point *ptr = new Point[2];//创建对象数组
	ptr[0].move(5, 10);//通过指针访问数组元素的成员
	ptr[1].move(15, 20);//通过指针访问数组元素的成员
	cout << "deleting............" << endl;
	delete[] ptr;//删除整个对象数组
	return 0;
}

  • 动态创建多维数组
语法形式:
new 类型名T[第一维长度][第二维长度]...;
如果内存申请成功,new运算返回一个指向新分配内存首地址的指针
例:
char(*fp)[3];
fp = new char [2][3];
#include <iostream>
using namespace std;

int main()
{
	int(*cp)[9][8] = new int[7][9][8];
	for (int  i = 0; i < 7; i++)
	{
		for (int j = 0;j < 9;j++) {
			for (int k = 0; k < 8; k++)
			{
				*(*(*(cp + i) + j) + k) = (i * 100 + j * 10 + k);
			}
		}

	}
	for (int i = 0; i < 7; i++)
	{
		for (int j = 0;j < 9;j++) {
			for (int k = 0; k < 8; k++)
			{
				cout << cp[i][j][k] << " ";
			}
			cout << endl;
		}
		cout << endl;
	}
	delete[] cp;
	return 0;
}


7月21日 P103

  • 动态内存分配与释放内存
  • 动态申请内存操作符 new
nre 类型名T(初始化参数类表)
功能:
在程序执行期间,申请用于存放T类型对象的内存空间
并依初值列表赋以初值
结果值:
成功,T类型的指针,指向新分配的内容
失败:抛出异常
  • 释放内存的操作符delete
delete 指针p
功能:释放指针p所指向的内存
p必须是new操作的返回值
#include <iostream>
using namespace std;

class Point {
public :
	Point() :x(0), y(0) {
		cout << "default construuctor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "constructor called." << endl;
	}
	~Point()
	{
		cout << "destructor called." << endl;
	}
	int getX()const { return x; }
	int getY()const { return y; }
	void move(int newX, int newY) {
		x = newX;
		y = newY;
	}
private:
	int x, y;
};

int main()
{
	cout << "step one:" << endl;
	Point *ptr1 = new Point;//调用默认构造函数
	delete ptr1;//删除对象,自动调用析构函数
	cout << "step tow:" << endl;
	ptr1 = new Point(1, 2);
	delete ptr1;

	return 0;
}


7月20日 P102

  • 对象指针
对象指针的定义形式
类名 *对象指针名;
例:
point a(5,10);
	point *pte;
ptr = &a;
通过指针访问对象成员
对象指针名->成员名
例
ptr->getx()相当于(*ptr).getx();
#include <iostream>
using namespace std;
class Point {
public:
	Point(int x = 0,int y = 0):x(x),y(y){}
	int getX()const { return x; }
	int getY()const { return y; }
private:
	int x, y;
};
int main()
{
	Point a(4, 5);
	Point *p1 = & a;//定义对象指针,用a的地址初始化
	cout << p1 -> getX() << endl;//用指针访问对象成员
	cout << a.getX() << endl;//用对象名访问对象成员
	return 0;
}
  • this指针
隐含于类的每一个非静态成员函数中
支出成员函数所操作的对象
	当通过一个对象调用成员函数时,系统先将该对象的地址赋值给this
指针,然后调用成员函数,成员函数对对象的数据成员进行操作时
就隐含使用了this指针
例如:Point类的getX函数中的语句:
return x;
相当于
return this -> x;
  • 错例
class Fred;//前向引用声明
class Barney {
	Fred x; //错误:类Fred的声明尚不完善
};
class Fred {
	Barney y;
};
  • 对例
class Fred;//前向引用声明
class Barney {
	Fred *x; //动态内存分配
};
class Fred {
	Barney y;
};


7月19日 P101

  • 指向函数的指针
  • 函数指针的定义
定义形式
存储类型 数据类型 (*函数指针名)();
含义
函数指针指向的是程序代码存储区
  • 函数指针的典型用途—实现函数回调
通过函数指针调用的函数
力图将函数的指针作为参数传递给一个函数,使得在处理
相似事件的时候可以灵活的使用不同的方法
调用者不关心谁是被调用者
须知道存在一个具有特定原型和限制条件的被调用函数
  • 函数指针举例
#include <iostream>
using namespace std;

int computer(int a,int b,int(*func)(int,int))
{
	return func(a, b);
}
int max(int a, int b)//求最大值
{
	return((a > b) ? a : b);
}
int min(int a, int b)//求最小值
{
	return((a < b) ? a : b);
}
int sum(int a, int b)//求和
{
	return a + b;
}
int main()
{
	int a, b, res;
	cout << "请输入整数a:";
	cin >> a;
	cout << "请输入整数b:";
	cin >> b;
	res = computer(a, b, &max);
	cout << "max of " << a << " and " << b << " is " << res << endl;
	res = computer(a, b, &min);
	cout << "min of " << a << " and " << b << " is " << res << endl;
	res = computer(a, b, &sum);
	cout << "sum of " << a << " and " << b << " is " << res << endl;
	return 0;
}

7月18日 P100

  • 指针类型的函数
若函数的返回值是指针,该函数就是指针类型函数
  • 指针函数的定义形式
存储类型  数据类型 *函数名()
{
函数体
}
  • 不要将非静态局部地址用作函数的返回值
  • 不要用非法地址
错误的例子:
在子函数中定义局部变量后将其地址返回给主函数,就是非法地址
  • 错误的例题
int main(){
	int *function();
	int *ptr = function();
	*ptr = 5;//危险的访问
	return 0; 
}
int *function(){
	int local = 0;//非静态局部变量作用域和寿命都仅限于本函数体内
	return &local;
}//函数运行结束时,变量local被保释
  • 正确的例子
主函数中定义的数组,在子函数中对该数组元素进行某种操作后,返回其中一个元素的地址
这就是合法有效的地址

在子函数中通过动态内存分配new造作取得的内存地址返回给主函数是合法有效的
但是内存分配和释放不在同一级别,要注意不能忘记释放,避免内存泄漏
#include <iostream>
using namespace std;

int main()
{
	int array[10];//主函数中定义的数组
	int* search(int* a, int num);
	for (int i = 0; i < 10; i++)
	{
		cin >> array[i];
	}
	int *zeroptr = search(array, 10);//将主函数中数组的首地址传给子函数
	return 0;
}
int *search(int *a, int num) {//指针a指向函数中定义的数组
	for (int i = 0; i < num; i++)
	{
		if (a[i] == 0) {
			return &a[i];//返回的地址指向的元素是在主函数中定义的
		}
		
	}
  • 例2
#include <iostream>
using namespace std;
int main()
{
	int* newintvar();
	int* intptr = newintvar();
	*intptr = 5;//访问的是合法有效的地址
	delete intptr;//如果忘记在这里释放,会造成内存泄漏
	return 0;
}
int* newintvar() {
	int* p = new int();
	return p;//返回的地址指向的是动态分配的空间
}//函数运行结束时,p中的地址任然有效

7月17日 P99

  • 以指针作为函数参数
  • 为什么要用指针做参数?
需要数据双向传递时
西药传递一组数据,只传首地址运行效率比较高
  • 例——读入三个浮点数,将整数部分和小数部分分别输出
#include <iostream>
using namespace std;



//将实数x分成整数部分和小数部分,形参intpart、farcpart是指针
void splitFloat(float x, int *intPart, float *fracPart) {
	*intPart = static_cast<int>(x);//取x的整数部分
	*fracPart = x - *intPart;//取出x的小数部分
}
int main()
{
	cout << "enter 3 float point numbers:" << endl;
	for (int  i = 0; i < 3; i++)
	{
		float x, f;
		int n;
		cin >> x;
		splitFloat(x, &n, &f);//变量地址作为实参
		cout << "intteger part = " << n << "   Fraction part = " << f << endl;
	}
	return 0;
}

  • 指向常量的指针做形参
#include <iostream>
using namespace std;
const int N = 6;
void print(const int *p, int n);
int main()
{
	int  array[N];
	for (int  i = 0; i < N; i++)
	{
		cin >> array[i];
	}
	print(array,N);


	return 0;
}

void print(const int *p, int n) {
	cout << "{ " << *p;
	for (int i = 1; i < n; i++)
	{
		cout << "," << *(p + i);
	}
	cout << " }" << endl;
}


7月16日 P97-P98

  • 用指针访问数组元素
  • 定义指向数组元素的指针
定义与赋值
int a[10],*pa;
pa =&a[0]或pa = a;
等效的形式
*pa 就是 a[0]
*(pa+1)就是a[1]
*(pa +i)就是 a[i]
a[i],*(pa+i),*(a+i),pa[i]都是等效的
  • 指针数组
  • 数组的元素是指针类型
Point *pa[2]
由pa[0]、pa[1]两个指针组成
#include <iostream>
using namespace std;
int main()
{
	int line1[] = { 1,0,0 };//矩阵的第一行
	int line2[] = { 0,1,0 };//矩阵的第二行
	int line3[] = { 0,0,1 };//矩阵的第三行
	//定义整形指针数组并初始化
	int *pline[3] = { line1,line2,line3 };
	cout << "Matrix test" << endl;
	//输出矩阵
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			cout << pline[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

7月15 P96续

  • 实验续
  • employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

#include<iostream>
using namespace std;



class  Employee {
	const char* name;
	const char* address;
	const char* city;
	const char* code;
public:
	const Employee(const char* n = "",const char* add = "",const char* ct = "" ,const char* cd = "")
		:name(n),address(add),city(ct),code(cd){}

	const void display() {
		cout << "name:" << name << endl;
		cout << "address:" << address << endl;
		cout << "city:" << city << endl;
		cout << "code:" << code << endl;
	}
	const void change_name(const char* nm) {
		name = nm;
	}
};
#endif
  • main
#include "stdafx.h"

#include <iostream>
#include"employee.h"
using namespace std;
int main()
{
	Employee e("Wang.Er", "Haidian", "BeiJing", "100084");
	e.display();
	e.change_name("Li San");
	e.display();
	return 0;
}

7月14日 P96

  • 实验1
#include <iostream>
#include<cmath>
using namespace std;
void swap(int &a, int &b) {//引用   比指针更安全
	int  temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a[3][3];
	cout << "输入9个整数作为矩阵元素值" << endl;
	//二维数组  两个for循环嵌套
	for (int  i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			cin >> a[i][j];
		}
	}
	cout << "初始矩阵:" << endl;
	for (int i = 0; i < 3; i++)
	{
		for (int  j = 0; j < 3; j++)
		{
			cout << a[i][j] << " ";

		}
		cout << endl;
	}
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < i; j++)
		{
			swap(a[i][j], a[j][i]);
		}

	}
	cout << "转置后的矩阵:" << endl;
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			cout << a[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}
   

7月13日 P95

  • 综合实例 个人银行账户管理程序
一个活期储蓄账户:
信息:
账号(id) 余额(balance) 年利率(rate)等
操作:
显示账户信息(show)  存款(deposit)  取款(withdraw)
结算利息(settle)等
  • 实例
#include <iostream>
#include <cmath>
using namespace std;

class SavingsAccount {//储蓄账户类
private:
	int id;         //账号
	double balance;  //余额
	double rate;     //存款年利率
	int lastDate;    //上次变更余额的日期
	double accumulation; //余额按日累加之和

	//记录一笔账,date为日期,amount为余额  desc为说明
	void record(int date, double amount);
	//获取到指定日期为止的存款金额按日累计值
	double accumlate(int date)const {
		return accumulation + balance * (date - lastDate);
	}
public:
	//构造函数
	SavingsAccount(int date, int id, double rate);
	int getId() { return id; }
	double getBalance() { return balance; }
	double getRate() { return rate; }


	//存入现金
	void deposit(int date, double amount);
	//取出现金
	void withdraw(int date, double amount);
	//结算利率,每年1月1日调用一次该函数
	void settle(int date);
	//显示账户信息
	void show();
};
//SavingsAccount类相关成员函数实现
SavingsAccount::SavingsAccount(int date,int id,double rate)
	:id(id),balance(0),rate(rate),lastDate(date),accumulation(0){
	cout << date << "\t#" << id << " is created" << endl;
}

//原有信息
void SavingsAccount::record(int date, double amount) {
	accumulation = accumlate(date);
	lastDate = date;
	amount = floor(amount * 100 + 0.5) / 100;//保留小数点后两位数
	balance += amount;
	cout << date << "\t#" << id << "\t" << amount << "\t" << balance << endl;
}
//存钱
void SavingsAccount::deposit(int date, double amount)
{
	accumulation = accumlate(date);
	lastDate = date;
	amount = floor(amount * 100 + 0.5) / 100;//保留小数点后两位数
	balance += amount;
	cout << date << "\t#" << id << "\t" << amount << "\t" << balance << endl;
}
//取钱
void SavingsAccount::withdraw(int date, double amount)
{
	accumulation = accumlate(date);
	lastDate = date;
	amount = floor(amount * 100 + 0.5) / 100;//保留小数点后两位数
	balance -= amount;
	cout << date << "\t#" << id << "\t" << -amount << "\t" << balance << endl;
}
//结算
void SavingsAccount :: settle(int date) {
	double interest = accumlate(date)*rate / 365;//计算年利息
	if (interest != 0)
	{
		record(date, interest);
		accumulation = 0;
	}
}
//显示
void SavingsAccount::show() {
	cout << "#" << id << "\tBalance:" << balance;
}
//主函数
int main()
{
	//建立两个账户
	SavingsAccount sa0(1, 21325302, 0.015);
	SavingsAccount sa1(1, 58320212, 0.015);

	//几笔账目
	sa0.deposit(5,5000);
	sa1.deposit(25,10000);
	sa0.deposit(45, 5500);
	sa1.withdraw(60, 4000);
	
	
	//90天后
	sa0.settle(90);
	sa1.settle(90);
	//输出各个账户信息
	sa0.show();
	cout << endl;
	sa1.show();
	cout << endl;

	return 0;
}

7月12日 P94

  • 指针的算数运算和关系运算
  • 指针的算数运算
指针与整数的加减运算
指针++、--运算
  • 指针类型的算数运算
指针P加上或减去N
其意义是指针当前指向位置的前方或后方第N个数据的起始位置
指针的++、--
意义是指向下一个或前一个完整数据的起始
运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置
当指针指向连续存储的同类型数据时,指针与整数的加减运算和自增自减预算才有意义
<pre>
*指针类型的关系运算
<pre>
指向相同类型数据的指针之间可以进行各种关系运算
指向不同数据类型的指针,以及指针与一般整数变量之间的关系
运算是无意义的
指针可以和零之间进行等于或不等于的关系运算
例:
p == 0或 p != 0

7月11日 P93

  • 指针的初始化和赋值
  • 指针变量的初始化
语法形式:
存储类型  数据类型 *指针名 = 初始地址;
例:
int *pa = &a;
注意事项:
用变量地址作为初始值时,该变量必须在指针初始化之前已经声明过
且变量类型应与指针类型一致
可以用一个已有合法的指针去初始化另一个指针变量
不要用一个内部非静态变量去初始化 static 指针
  • 指针变量的赋值运算
语法形式:
指针名 = 地址
注意:
“地址”中存放的数据类型与指针类型必须相符
向指针变量赋的值必须是地址常量或变量,不能是普通整数
例如:
通过地址运算“&”求得已定义的变量和对象的起始地址
动态内存分配成功时返回的地址
例外:
整数0可以赋给指针,表示空指针
允许定义或声明指向void类型的指针。该指针可以被赋予
任何类型对象的地址
例:
void *general;
  • 指针空值 nullptr
#include <iostream>
using namespace std;
int main()
{
	int i;  //定义int型数i
	int *ptr = &i;  //取i的地址赋给ptr
	i = 10;    //int型赋值
	cout << "i = " << i << endl; //输出int型
	cout << "*ptr = " << *ptr << endl;//输出*ptr指针所指地址的内容
	return 0;
}
  • void指针
#include <iostream>
using namespace std;
int main()
{
	//!void voidobject;  错误,不能声明void类型变量
	void *pv;   //对,可以声明void类型指针
	int  i = 5;
	pv = &i;    //void类型指针指向整形变量
	int *pint = static_cast<int *>(pv); //void指针转换为ing指针
	cout << "*pint = " << *pint << endl;
	return 0;
}
  • 指向常量的指针
const指针
不能通过指向常量的指针改变所指对象的值,但是指针
本身可以改变,可以指向另外的对象
例:
int a;
const int *p1 = &a;  //p1是指向常量的指针
int b;
p1 = &b;  //正确  p1本身的只可以改变
*p1 = 1;//编译时出错,不能通过p1改变所指的对象
  • 指针类型的常量
若声明指针常量,则指针本身的值不能被改变
例:
int a;
int *const p2 = &a;
p2 = &b;  //错误,p2是指针常量,值不能改变

7月10日 P92

  • 指针的概念、定义和指针运算
  • 内存空间的访问方式
通过变量名访问
通过地址访问
  • 指针的概念
指针:内存地址,用于间接访问存储单元
指针变量:用于存放地址的变量
static int i;
static int* ptr = &i;
ptr 是指向int变量的指针
  • 于地址相关的运算 “ * ”和“ & ”
指针运算符:*
地址运算符:&

7月9日 P91

  • 基于范围的for循环
int main()
{
int array[3] = {1,2,3};
int *p;
for(p = array;p<array+sizeof(array)/sizeof(int);++p)
{
*p = 2
cout <<*p<<endl;
}
return 0;
}
对比
int main()
{
int array[3]= {1,2,3};
for(int &e:array)
{
e +=2;
cout<<e<<endl;
}
return 0;
}

7月8日 P90

  • 对象数组
  • 对象数组的定义与访问
定义对象数组:
类名 数组名[元素个数]
访问对象数组元素:
通过下标访问。数组名[下标].成员名
  • 对象数组的初始化
数组中每一个元素对象被创建时,系统都会调用类构造函数
初始化该对象
通过初始化列表赋值
例:point a[2] = {point(1,2),point(3,4)};
如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化
调用默认构造函数
  • 数组元素的构造和析构
创建数组是,元素所属的类未声明构造函数,则采用默认构造函数
各元素对象的初值要求为相同的值时,可以声明具有默认形参值得构造函数
各元素对象的初值要求为不同的值时,需要声明带形参的构造函数
当数组中每一个对象被删除时,系统都要调用一次析构函数
  • Point.h
//point.h
#pragma once
#ifndef _POINT_H


class Point{//类的定义
public ://外部接口
	Point();
	Point(int x, int y);
	~Point();
	void move(int newX, int nrewY);
	int getX() const { return x; }
	int getY() const { return y; }
	static void showCount();//静态函数成员
private://私有数据成员
	int x, y;
};
#endif _PO
  • point.cpp
//point.cpp
#include <iostream>
#include "Point.h"
using namespace std;
Point::Point() :x(0), y(0) {
	cout << "Default constructor called." << endl;
}
Point::Point(int x, int y) : x(x), y(y) {
	cout << "constructor called." << endl;
}
Point::~Point() {
	cout << "destructor called." << endl;
}
void Point::move(int newX, int newY) {
	cout << "moving the point to (" << newX << "," << newY << ")" << endl;
	x = newX;
	y = newY;
}

void Point::showCount()
{
}

  • main
#include <iostream>
#include "Point.h"
using namespace std;

int main()
{
	cout << "entering main..." << endl;
	Point a[2];
	for (int  i = 0; i < 2; i++)
	{
		a[i].move(i + 10, i + 20);
	}
	cout << "exiting main..." << endl;
	return 0;
}



7月7日 编译正确 计算错误 P89

  • 数组作为函数的参数
数组元素做实参,与单个变量是一样的
数组名作参数,形、实参数都应该是数组名,类型要一样,传送的是数组首地址。
对形参数组的改变会直接影响到实参数组
#include <iostream>
using namespace std;
//计算二维数组A每行的值的和
void rowSnm(int a[][4], int nRow) {
	for (int i = 0; i < nRow; i++) {
		for (int j = 0; j < 4; j++)
			a[i][0] += a[i][j];
	}
}
//主函数
int main()
{   
	//声明数组
	int table[3][4] = { {1,2,3,4},{2,3,4,5,},{3,4,5,6} };
	for (int i = 0; i < 3; i++)//输出数组
	{
		for (int j = 0; j < 4; j++)
			cout << table[i][j]<<"    ";
		cout << endl;
	}
	//调用子函数,计算各行和
	rowSnm(table, 3);
	//输出计算结果
	for (int  i = 0; i < 3; i++)
	{
		cout << "snm of row " << i << " is " << table[i][0] << endl;
	}
	return 0;
}

7月6日 P88

  • 一堆数组应用举例
#include <iostream>
using namespace std;
int main()
{
	const char key[] = { 'a','c','b','c','d' };
	const int NUM_QUES = 5;
	char c;
	int ques = 0, numCorrect = 0;
	cout << "enter the " << NUM_QUES << "question tests:" << endl;
	while (cin.get(c))
	{
		if (c != '\n')
		{
			if (c == key[ques]) {
				numCorrect++;
				cout << " ";
			}
			else
			{ 
				cout << "*";
			}
				
			ques++;
		}
		else		
		{
			cout << "score" << static_cast<float>(numCorrect) / NUM_QUES * 100 << "%";
			ques = 0;
			numCorrect = 0;
			cout << endl;
		}
		
		}
		return 0;
	}

7月5日 P87

  • 数组的存储与初始化
  • 一堆数组的存储
数组元素在内存中顺次存放,他们的地址是连续的
元素简物理地址上的相邻,对应着逻辑次序上的相邻
例:
int a[10];
a  a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
a 数组名字是数组首元素的内存地址
数组名是一个常量,不能被赋值
  • 一堆数组的初始化
列出全部元素的初始值
例:
static int a[10] = {0,1,2,3,4,5,6,7,8,9};
只给一部分元素指定初始值
例:
static int a[10] ={0,1,2,3,4};
列出全部数组元素初始值时,可以不指定数组长度
例:
static int a[] = {0,1,2,3,4,5,6,7,8,9};
  • 二维数组的存储
按行存放
例:
float a[3][4];
            a[0]      a00 a01 a02 a03
可以理解为a a[1]      a10 a11 a12 a13
            a[2]      a20 a21 a22 a23
其中数组a的存储顺序为:
a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23
  • 二维数组的初始化
将所有初始值写在一个{}内,按顺序初始化
例:
static int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
分行列出二维数组元素的初值
例:
static int a[3][4]={1,2,3},{4,5,6},{7,8,9},{10,11,12};
可以只对部分元素初始化
例:
static int a[3][4]={{1},{0,6},{0,0,11}} ;
列出全部初始值时,第一维下标个数可以省略
例:
static int a[][4]= {1,2,3,4,5,6,7,8,9,10,11,12};
或static int a[][4] ={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
如果不做任何初始化,局部作用域的非静态数组中会存在垃圾数据
static数组中的数据默认初始化为0
如果只对部分元素初始化,剩下的未显示初始化的元素
将自动被初始化为0

#include <iostream>
using namespace std;

int main()
{
	int f[20] = { 1,1 }; //初始化第0、1个数
	for (int i = 2; i < 20; i++) {//求2-19个数
		f[i] = f[i - 2] + f[i - 1];
		for (int i = 0; i < 20; i++){//输出每行5个数		
			if (i % 5 == 0)cout << endl;
			cout.width(12);//设置输出宽度为12
			cout << f[i];
		}
	}
	return 0;
}

7月4日 P85-P86

  • 数组的定义与使用
  • 数组是具有一定顺序关系的若干相同类型变量的集合体

组成数组的变量称为该数组的元素

  • 数组的定义
语法:
数组说明符 数组名[常量表达式][常量表达式]...
数组名的构成方法与一般变量名相同
例:
int a[10];
表示a为整形数组,有10个元素:a[0]...a[9]
例2:
int a[5][3];
表示a为整形二维数组,其中第一维有5个下标(0~4),
第二维有3个下标(0~2),整数的元素个数为15,
可以用于存放5行3列的整形数据表格
  • 数组元素的使用
数组必须先定义后使用
可以逐个引用数组元素
例:
a[0]=a[5]+a[7]-a[2*3]
b[1][2] = a[2][3]/2
#include <iostream>
using namespace std;
int main()
{
	int a[10], b[10];
	for (int i = 0;i < 10;i++) 
	{
		a[i] = i * 2 - 1;
		b[10 - i - 1] = a[i];
	}
	for (int  i = 0; i < 10; i++)
	{
		cout << " a[ " << i << " ]= " << a[i] << endl;
		cout << " b[ " << i << " ]= " << b[i] << endl;
	}
	return 0;
}

7月3日 P84

  • 实验续
#ifndef CLIENT1_H_
#define CLIENT1_H_
#endif // !CLIENT1_H_

class Client {
	static char ServerName;
	static int ClientNum;
public:
	static void ChangeServerName(char name);
	static int getClientNum();
};
#include"client.h"


void Client::ChangeServerName(char name) {
	Client::ServerName = name;
	Client::ClientNum++;
}
int Client::getClientNum() {
	return Client::ClientNum;
}
#include"client.h"
#include <iostream>
using namespace std;
int Client::ClientNum = 0;
char Client::ServerName = 'a';
int main()
{
	Client c1;
	c1.ChangeServerName('a');
	cout << c1.getClientNum() << endl;
	Client c2;
	c2.ChangeServerName('b');
	cout << c2.getClientNum() << endl;
	return 0;
}

7月2日 P83

  • 小结
  • 主要类容
对象的生存、作用域与可见性
类的静态成员
数据的共享与保护
友元
编译预处理命令
多文件结构和工程
  • 实验
#include <iostream>
using namespace std;
void fn1();
int x = 1, y = 2;
int main()
{
	cout << "begin..." << endl;
	cout << "x = " << x << endl;
	cout << " y = " << y << endl;


	cout << "evaluate x and y in main().." << endl;
	int x = 10, y = 20;
	cout << "x = " << x << endl;
	cout << "y = " << y << endl;

	cout << "step into fn1().." << endl;
	fn1();
	cout << "back in main" << endl;
	cout << "x = " << x << endl;
	cout << "y = " << y << endl;
	return 0;
}
void fn1()
{
	int y = 200;
	cout << "x = " << x << endl;
	cout << "y = " << y << endl;

}

7月1日 P82

  • 多文件结构和预编译命令
  • C++程序的一般组织结构
一个工程文件可以划分为多个源文件
例如:
类声明文件(.h文件)
类实现文件(.cpp文件)
类的使用文件(main()所在的.cpp文件)
利用工程来组合各个文件
  • 外部函数
在所有类之外声明的函数(非成员函数),都是具有文件作用域的
这样的函数都可以在不同的编译单元中被调用
只要在调用之前进行引用声明(指声明函数原型)即可
  • 将变量和函数限制在编译单元内
在你匿名命名空间中定义的变量核函数
都不会暴露给其他的编译单元
namespace{//匿名命名空间
	int n;
	void f(){
	n++;
	}
}
  • 预编译处理
#include 包含指令
   将一个源文件嵌入到当前源文件中该点处
#include <文件名>
   按标准方式搜索,文件位于C++系统目录的include子目录下
@include "文件名"
   首先在当前目录中搜索,若没有,再按标准方式搜索
#include 宏定义指令
   定义符号常量,很多情况下已被const定义语句取代
   定义带参数宏,已被内联函数取代
#undef
   删除有#define定义的宏,使之不再起作用

6月30日 P81

  • 共享数据的保护
  • 常类型
常对象:必须进行初始化,不能被更新
const  类名 对象名;
常成员:用const进行修饰的类成员:常数据成员和常函数成员
常引用:被引用的对象不能被更新
const 类型说明符 &引用名;
常数组:数组元素不能被更新
类型说明符  const  数组名[大小]
常指针:
指向常量的指针 
  • 常对象
用const修饰的对象
例:
class A
{
public:
A(int i,int j){x = i;y = j;}
...
private:
int x,y;
//...
A const a(3,4);//a是常对象不能被更新
  • 常成员
用const 修饰的对象成员
常成员函数:
使用const关键字说明的函数
常成员函数不更新对象的数据成员
常成员函数说明格式:
类型说明符  函数名(参数表) const;
这里,const是函数类型的一个组成部分,因此实现部分也要带const关键字
const关键字可以被用于参与对重载函数的区分
通过常对象只能调用它的常成员函数
常数据成员:
使用const说明的数据成员
#include <iostream>
using namespace std;
class R {
public:
	R(int  r1, int r2) :r1(r1), r2(r2) {}
	void print();
	void print() const;
private:
	int r1, r2;
};
void R::print() {
	cout << r1 << ":" << r2 << endl;
}
void R::print() const{
	cout << r1 << ":" << r2 << endl;
}
int main()
{
	R a(5, 4);
	a.print();//调用void print()
	const R b(20, 52);
	b.print();//调用void print() const
	return 0;
}

  • 常数据成员
#include <iostream>
using namespace std;
class A {
public:
	A(int i);
	void pring();
private:
	const int a;
	static const int b;//静态常数据成员
};
const int A::b = 10;
A::A(int i):a(i){}
void A::pring() {
	cout << a << ":" << b << endl;
}
int main()
{
    //建立对象a和b,并以100和0作为初始值,分别调用构造函数
	//通过构造函数的初始化列表给对象的常数据成员赋初值
	A a1(100), a2(0);
	a1.pring();
	a2.pring();
	return 0;
}

  • 常引用
在友元函数中用常引用做参数
既能获得较高的执行效率
又能保证实参的安全性
class Point {//类定义
public://外部接口
	Point(int x = 0,int y = 0):x(x),y(y){}
	int getX() { return x; }
	int getY() { return y; }
	friend float dist(const Point &p1, const Point &p2);
private://私有成员
	int x, y;
};
float dist(const Point &p1, const Point &p2) {
	double x = p1.x - p2.x;
	double y = p1.y - p2.y;
	return static_cast<float>(sqrt(x*x + y * y));
}
int main()
{
	const Point myp1(1, 1), myp2(4, 5);
	cout << "the distance is:";
	cout << dist(myp1, myp2) << endl;
	return 0;
}

6月29日 P80

  • 类的友元
友元是C++提供的一种破坏数据封装和数据隐藏的机制
通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本被隐藏的信息
可以声明友元函数和友元类
为了确保数据的完整性,及数据封装与隐藏的原则,建议慎用友元
  • 友元函数
友元函数实在类中声明由关键字friend修饰说明的非成员函数
在它的函数体中能够通过对象名访问private和protected成员
作用是增加灵活性,使程序员可以在封装和快速性方面做合理选择
访问对象中的成员必须通过对象名
<pre>
例
<pre>
#include <iostream>
#include<cmath>
using namespace std;
class Point {//point 类声明
public://外部接口
	Point(int x = 0,int y = 0):x(x),y(y){}
	int getX() { return x; }
	int gety() { return y; }
	friend float dist(Point & a, Point & b);
private://私有数据成员
	int x, y;
};
float dist(Point &a, Point & b) {
	double x = a.x - b.x;
	double y = a.y - b.y;
	return static_cast<float>(sqrt(x*x + y * y));
}

int main()
{
	Point P1(1, 1), P2(4, 5);
	cout << "the distance is:";
	cout << dist(P1, P2) << endl;
	return 0;
}
  • 友元类
若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员
声明语法:将友元类各在另一个类中使用friend修饰说明
友元的关系是单向的:
声明B类是A类的友元(不等于)A类是B类的友元

6月28日 P79

  • 类的静态函数成员
静态函数成员主要用于处理该类的静态数据
静态函数成员如果访问非静态成员
要通过对象来访问

#include <iostream>
using namespace std;
class Point {
public:
	Point(int x = 0, int y = 0) :x(x), y(y) {//构造函数
		count++;
	}
	Point(Point & p) {//复制构造函数
		x = p.x;
		y = p.y;
		count++;
	}
	~Point()
	{
		count--;
	}
	int getX() { return x; }
	int getY() { return y; }
	static void showCount() {
		cout << "obj count = " << count << endl;
	}
private:
	int x, y;
	static int count;//静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员的定义和初始化,使用类名限定
int main()
{
	Point::showCount();//输出对象个数
	Point a(4, 5);//定义对象a,其构造函数会使count增1
	cout << "point a:" << a.getX() << "," << a.getY();
	Point::showCount();//输出对象个数
	Point b(a);//定义对象b,期构造函数会使count增1
	cout << "point b:" << b.getX() << "," << b.getY();
	Point::showCount();//输出对象个数

	return 0;

}

6月27日 P78

  • 类的静态数据成员
用关键字static声明
为该类的所有对象共享,静态数据成员具有静态生存期
必须在类外定义和初始化,采用(::)类指明所属的类
#include <iostream>
using  namespace std;

class Point {//Point 类的定义
public://外部接口
	Point(int x = 0, int y = 0) : x(x), y(y) {//构造函数		
		count++;//在构造函数中对count累加,所有对象共同维护同一个count
	}
	Point(Point & p) { //复制构造函数
		x = p.x;
		y = p.y;
		count++;
	}
	~Point() { count--; }
	int getX() { return x; }
	int   getY() { return y; }
	void showCount() {  //输出静态数据成员
		cout << "Object count = " << count << endl;
	}
private://私有数据成员
	int x, y;
	static int count;//金泰数据成员声明,用于记录的个数
};
int Point::count = 0;//金泰数据成员定义和初始化,使用类名限定
int main()//主函数
{
	Point a(4, 5);//定义对象a,其构造函数会使count增加1
	cout << "point A :" << a.getX() << "," << a.getY();
	a.showCount();//输出对象个数
	Point b(a);//定义对象b构造函数会使count增加1
	cout << "Point B: " << b.getX() << "," << b.getY();
	b.showCount();//输出对象个数
	return 0;
}




6月26日 P77

  • 对象的生存期
对象从产生到结束的这段时间,就是他的生存期
在对象生存期内,对象将保持它的值,直到被更新为止
  • 对象和变量的的生存期分为静态生存期和动态生存期
  • 静态生存期
这种生存期与程序的运行期相同。
在文件作用域中声明的对象具有这种生存期
在函数内部声明静态生存期对象,要冠以关键字static
  • 动态生存期
开始于程序执行到声明点时,
结束于命名该标识符的作用域结束处
块作用域中声明的,没有用static修饰的对象
试灯台生存期的对象(习惯称为局部生存期对象)
#include <iostream>
using namespace std;
int i = 1;//i  为全局变量,具有静态生存期
void other() {
	static int a = 2;
	static int b;
	//a,b为静态局部变量,具有全局寿命,局部可见
	//只有第一次进入换书是被初始化
	int c = 10;//c为局部变量,具有动态生存期
	//每次进入函数时被初始化
	a += 2;i += 32;c += 5;
	cout << "---OTHER---\n";
	cout << " i:" << i << " a:" << a << " b:" << b << " c:" << c << endl;
	b = a;
}
int main()
{
	static int a;//静态局部变量,有全局寿命,局部可见
	int b = -10;//b,c为局部变量,具有动态生存期
	int c = 0;
	cout << "---MAIN---\n";
	cout << " i:" << i << " a:" << a << " b:" << b << " c:" << c << endl;
	c += 8;
	other();
	cout << "---MAIN---\n";
	cout << " i:" << i << " a:" << a << " b:" << b << " c:" << c << endl;
	i += 10;
	other();
	return 0;
}

6月25日 P76

  • 标识符的作用域与可见性
  • 作用域分类
函数原型作用域
局部作用域(块作用域)
类作用域
文件作用域
命名空间作用域
  • 函数原型作用域
函数原型中的参数
其作用域始于“(”结束于“)”
例:
double area(double radius);
radius的作用域仅在于此处,不能用于程序正文其他地方
  • 局部作用域
函数的形参,在块中声明的标识符;
作用域自声明处起,限于块中
例:
void fun(int a){                                     //
	int b;                       //              //
	cin >>b;                     //              //
	if(b >0){                    //              //a的作用域
		int c;  //           // b的作用域    //
		...     //c的作用域  //              //
	}               //           //              //
}                                    //              //
  • 类作用域
类的成员具有类作用域
其范围包括类体和成员函数体
在类作用域以外访问类的成员
静态成员:
通过类名,或者该类的对象名、对象引用访问
非静态成员:
通过类名,或者该类的对象名、对象引用、对象指针访问
  • 文件作用域
不在前述各个作用域中出现的声明
就具有稳健作用于
其作用域开始于声明点,结束于文件尾
可见性:
可见性是冲对标识符引用角度来谈的概念
可见性表示从内层作用域向外层作用域“看”时能看见什么
如果标识在某处可见,就可以在该处引用此标识符
  • 命名空间作用域
////////////////////////
//     类作用域       //
//  ///////////////   //
//  // 块作用域  //   //
////////////////////////
可见性:
如果某个标识符在外层中声明
但在内层中没有同一标识符的声明
则该标识符在内层可见
对于两个嵌套的作用域,如果在内层的作用域内声明了
与外层作用域中同名的标识符
则外层作用域的标识符在内层不可见

例:

#include <iostream>
using namespace std;

int i;//全局变量,文件作用域

int main()
{
	i = 5;//为全局变量i赋值
	{
		int i;//局部变量,局部作用域
		i = 7;
		cout << "i = " << i << endl; //输出7
	}
	cout << "i = " << i << endl;//输出5
	return 0;
}

6月24日 P75

  • 数据的共享与保护
  • 变量和对象的定义在不同的位置
函数体内、类体内、函数原型参数表内、所有函数和类之外
起作用域、可见性、生存期都不同
  • 属于整个类的数据成员
静态数据成员
  • 用于处理静态数据成员的函数
静态成员函数
  • 友元——关键字friend
对一些类外的函数、其他的类,给预授权,是只可以访问类的私有成员
  • 通过const关键字,限制对共享数据的修改

6月23日 P74实验

#include <iostream>
using namespace std;
//声明枚举类型CPU_Rank
enum CPU_Rank { P1 = 1, P2, P3, P4, P5, P6, P7 };
class CPU
{
	//私有数据成员
private:
	//等级rank
	CPU_Rank rank;
	//频率
	int frequency;
	//电压
	float voltage;
	//公有成员
public:
	//构造函数
	CPU(CPU_Rank r, int f, float v)
	{
		rank = r;
		frequency = f;
		voltage = v;
		cout << "构造了一个CPU!" << endl;
	}
	//析构函数
	~CPU() { cout << "析构了一个CPU!" << endl; }
	//外部接口函数
	CPU_Rank GetRank()const { return rank; }
	int GetFrequency()const { return frequency; }
	float GetVoltagge()const { return voltage; }

	void SetRank(CPU_Rank r) { rank = r; }
	void GetFrequency(int f) { frequency = f; }
	void GetVoltagge(float v) { voltage = v; }
	//
	void Run() { cout << "CPU开始运行!" << endl; }
	void Stop() { cout << "CPU停止运行!" << endl; }
};
enum RAM_Type { DDR2 = 2, DDR3, DDR4 };
class RAM
{
private:
	enum RAM_Type type;
	unsigned int frequency;//MHz
	unsigned int size;//GB
//构造函数
public:
	RAM(RAM_Type t, unsigned int f, unsigned int s)
	{
		type = t;
		frequency = f;
		size = s;
		cout << "构造了一个RAM!" << endl;
	}
	~RAM() { cout << "析构了一个RAM!" << endl; }
	RAM_Type GetType()const { return type; }
	unsigned int GetFrequency()const { return frequency; }
	unsigned int GetSize()const { return size; }

	void SetType(RAM_Type t) { type = t; }
	void SetFrequency(unsigned int f) { frequency = f; }
	void SetSize(unsigned int s) { size = s; }

	void Run() { cout << "RAM开始运行" << endl; }
	void Stop() { cout << "RAM结束运行" << endl; }
};
enum CDROM_Interface{SATA, USB};
enum CDROM_Install_type { external, built_in };
class CD_ROM
{
private: 
	enum CDROM_Interface interface_type;
	unsigned int cache_size;//MB
	CDROM_Install_type install_type;
public:
	CD_ROM(CDROM_Interface i, unsigned int s, CDROM_Install_type it)
	{
		interface_type = i;
		cache_size = s;
		install_type = it;
		cout << "构造了一个CD_ROM!" << endl;
	}
	~CD_ROM()
	{
		cout << "析构了一个CD_ROM!" << endl;
	}
	CDROM_Interface GetInterfaceType()const { return interface_type; }
	unsigned int GetSize() const { return cache_size; }
	CDROM_Install_type GetInstallType() const { return install_type; }

	void SetInterfaceType(CDROM_Interface i) { interface_type = i; }
	void SetSize(unsigned int s) { cache_size = s; }
	void SetInstallType(CDROM_Install_type i) { install_type = i; }


	void Run() { cout << "CD_ROM开始运行!" << endl; }
	void Stop() { cout << "CD_ROM停止运行!" << endl; }

};
class COMPUER
{
private:
	CPU my_cpu;
	RAM my_ram;
	CD_ROM my_cdrom;
	unsigned int storage_size;//GB
	unsigned int bandwidth;//MB
public:

	COMPUER(CPU c, RAM r, CD_ROM cd, unsigned int s, unsigned int b);

	~COMPUER() { cout << "析构了一个COMPUTER!" << endl; }
	
	
	
	void Run() {
		my_cpu.Run();
		my_ram.Run();
		my_cdrom.Run();
		cout << "COMPUTER开始运行!" << endl;
	}
	void Stop() {
		my_cpu.Stop();
		my_ram.Stop();
		my_cdrom.Stop();
		cout << "COMPUTER结束运行!" << endl;
	}
	
};
COMPUER::COMPUER(CPU c, RAM r, CD_ROM cd, unsigned int s, unsigned int b) :my_cpu(c), my_ram(r), my_cdrom(cd)
{
	storage_size = s;
	bandwidth = b;
	cout << "构造了一个COMPUTER!" << endl;
}

int main()
{
	CPU a(P6, 300, 2.8);
	a.Run();
	a.Stop();
	cout << "**************************************\n";
	RAM b(DDR3, 1600, 8);
	b.Run();
	b.Stop();
	cout << "*********************************************\n";
	CD_ROM c(SATA, 2, built_in);
	c.Run();
	c.Stop();
	cout << "*******************************************\n";
	COMPUER my_computer(a, b, c, 128, 10);
	cout << "********************************************\n";
	my_computer.Run();
	my_computer.Stop();
	cout << "******************************************\n" << endl;

	return 0;



6月22日 P72-P73

  • 小结
  • 主要内容
面向对象的基本概念
类和对象的声明
构造函数
析构函数
内联成员函数
复制构造函数
类的组合
结构类
联合类
枚举类
UML
  • 实验例题
#include <iostream>
using namespace std;
//声明枚举类型CPU_Rank
enum CPU_Rank { P1 = 1, P2, P3, P4, P5, P6, P7 };
class CPU
{
	//私有数据成员
private:
	//等级rank
	CPU_Rank rank;
	//频率
	int frequency;
	//电压
	float voltage;
	//公有成员
public:
	//构造函数
	CPU(CPU_Rank r, int f, float v)
	{
		rank = r;
		frequency = f;
		voltage = v;
		cout << "构造了一个CPU!" << endl;
	}
	//析构函数
	~CPU() { cout << "析构了一个CPU!" << endl; }
	//外部接口函数
	CPU_Rank GetRank()const { return rank; }
	int GetFrequency()const { return frequency; }
	float GetVoltagge()const { return voltage; }

	void SetRank(CPU_Rank r) { rank = r; }
	void GetFrequency(int f) {  frequency = f; }
	void GetVoltagge(float v){ voltage = v; }
	//
	void Run() { cout << "CPU开始运行!" << endl; }
	void Stop() { cout << "CPU停止运行!" << endl; }
};
int main()
{
	CPU a(P6, 300, 2.8);
	a.Run();
	a.Stop();
	return 0;
}



6月21日 P71

  • 枚举类
  • 枚举类定义
语法形式:
enum  class 枚举类型名:底层类型{枚举值列表};
例:
enum class Type{General,Light,Medium,Heavy};
enum class Type:char{General,Light,Medium,Heavy};
enum class Category{General = 1,Pistol,MachineGun,Cannon};
  • 枚举类的优势
强制用域:
其作用域限制在枚举类中
例:
使用Type的枚举值General:
Type::General
转换限制:
枚举类对象不可以与整型隐式的相互转换
可以指定底层类型:
例:
enum class Type{General,Light,Medium,Heavy};
  • 使用枚举类比简单枚举类型的控制更加严格
  • 枚举类——示例
#include <iostream>
using namespace std;
enum class Side { Right, Left };
//如果用简单枚举类型,会发生冲突
//使用枚举类则不发生冲突
enum class Thing { Wrong, Right };//不冲突
int main()
{
	Side s = Side::Right;
	Thing w = Thing::Wrong;
	//会报错,s和w属于不同的枚举类,无法直接用来做比较
	cout << (s == w) << endl;//编译错误,无法直接比较不同枚举类
	return 0;
}



6月20日 P70

  • 联合体
  • 定义形式
union 联合体名称{
	公有成员
protected:
	保护型成员
private:
	私有成员
};
  • 特点
成员共用一组内存单元
任何两个成员不会同时有效
  • 联合体的内存分配
举例说明:
union Mark{//显示成绩的年合体体
	char grade;//等级制的成绩
	bool pass;//只记是否通过课程的成绩
	ont percent;//百分制的成绩
};
Mark 4个字节 grade 1个字节 pass 1个字节 percent 4个字节
最多字节数的成员来分配空间
  • 无名联合
例:
union{
	int i;
	float f;
}
在程序中可以这样使用:
	i = 10;
	f = 2.2;
  • 代码——使用联合体保存成绩,并且输出
#include <iostream>
#include <string>
using namespace std;


class ExamInfo {
public:
	ExamInfo(string name, char grade) :name(name), mode(GRADE), grade(grade) {}
	ExamInfo(string name,bool pass):name(name),mode(PASS),pass(pass){}
	ExamInfo(string name, int percent) :name(name), mode(PERCENTAGE), percent(percent) {}
	void show();
private:
	string name; //课程名称
	enum { GRADE, PASS, PERCENTAGE } mode;//计分方式
	union 
	{
		char grade;//等级制的成绩
		bool pass;//只记是否通过课程的成绩
		int percent;//百分制的成绩
	};
};
void ExamInfo::show() {
	cout << name << " : ";
	switch (mode)
	{
	case ExamInfo::GRADE: cout << grade;
		break;
	case ExamInfo::PASS:cout << (pass ? "PASS":"FAIL");
		break;
	case ExamInfo::PERCENTAGE:cout << percent;
		break;
	}
	cout << endl;
}
int main()
{
	ExamInfo course1("English", 'B');
	ExamInfo course2("Calculus", true);
	ExamInfo course3("C++ programming", 85);
	course1.show();
	course2.show();
	course3.show();
	return 0;
}


6月19日 P68-P69

  • UML简介
  • UML有三个基本部分
事物(things)
关系(Relationships)
图(Diagrams)
  • 包含关系——聚集
共享聚集:部分可以参加多个整体
组成聚集(组合):整体拥有各个部分,整体与部分共存
如果整体不存在,部分也就不存在了
<pre>
*结构体
<pre>
结构体是一种特殊形态的类
与类唯一的区别:
类的缺省访问权限是private,
结构体的缺省访问权限是public。
  • 什么时候使用结构体而不用类
定义主要用来保存数据,而没有什么操作的类型
人们习惯将结构体的数据成员设为公有,因此这时用结构体更方便
  • 结构体的定义
struct 结构体名称{
公有成员
protected:
	保护型成员
private:
	私有成员
};
  • 结构体中可以有数据成员和函数成员
  • 结构体的初始化
如果:
一个结构体的全部数据成员是公共成员;
没有用户定义的构造函数;
没有基本类和虚函数
这个结构体的变量可以用以下的语法形式初始化
类型名  变量名 = { 成员数据1初值,成员数据2初值,.....}
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

//学生信息结构体
struct  Student
{
	int num;//学号
	string  name;//姓名  字符串对象
	char sex;//性别
	int age;//年龄
};
//主函数
int main()
{
	//列出出事的方式对结构体对象进行初始化
	Student stu = { 97001,"Lin Lin",'F',19 };
	//用结构体名.对象名进行访问
	cout << "学号: " << stu.num << endl;
	//因为结构体成员都是公共成员
	cout << "姓名: " << stu.name << endl;
	//所以在外部都可以用这种方式进行访问
	cout << "性别: " << stu.sex << endl;
	cout << "年龄: " << stu.age << endl;
	return 0;
}

P66

  • 类的组合程序
  • 函数的构造无法理解 编译失败

6月18日 P67

  • 前向引用声明
先声明后使用
如果需要在某个类的声明之前,引用该类,
则应该进行前向引用声明
前向引用声明职位程序引入一个标识符,
但具体声明在其他地方
  • 例1
class B;//前向引用声明
class A {
public :
	void f (B b);
};
class B {
public :
	void g(A a);
};
  • 前向引用声明注意事项
在提供一个完整的类声明之前,不能声明该类的对象,
也不能在内联成员函数中使用该类的对象
当使用前向引用声明时,只能使用被声明的符号,
而不能涉及类的任何细节
  • 例2
class Fred;//前向引用声明
class Barney {
	Fred x;//错误:类Fred的声明尚不完善
};
class Fred {
	Barney y;
};


6月17日 P65

  • 类的组合
  • 组合的概念
类中的成员是另一个类的对象
可以在已有抽象的基础上实现更复杂的抽象
  • 类组合的构造函数设计
原则:不仅要负责对本类中的基本类型成员数据初始化,也要对对象成员初始化
声明形式:
	类名::类名(对象成员所需的形参,本类成员形参);
	对象1(参数),对象2(参数),........
{
	//函数体其他语句
}
  • 构造组合类对象时的初始化次序
首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义一的次序。
成员对象构造函数调用顺序:按对象成员定义顺序,先声明者先构造。
初始化列表中未出现的成员对象,调用默认构造函数(即无形参的)初始化
处理完初始化列表之后,再执行构造函数的函数体

P63 没有完成

  • 构造函数无法理解 编译失败

6月16日 P64

  • 析构函数
析构函数完成对象被删除前的一些清理工作
如果程序中未声明析构函数
编译器将自动产生一个默认的析构函数
其函数体为空
系后汉书的原型:~类名();
析构函数没有参数,没有返回值
  • 析构函数举例
#include <iostream>
using namespace std;
class Point {
public:
     Point (int xx,int yy);
     ~Point();
private:
int x,y;
};
Point ::Point(int xx,int yy)
{
	x = xx;
	y = yy;
}
Point::~Point(){
}


6月15日 P61-P62

  • 委托构造函数
委托构造函数使用类的其他构造函数执行初始化过程
例如:
Clock (int newH, int newM, int newS):
hour(newH), minute(newM), second(newS) {}
Clock():Clock(0,0,0){}
  • 复制构造函数
  • 复制构造函数定义
复制构造函数是一种特殊的和构造函数,其形参为本类的对象引用。
作用是用一个也存在的对象去初始化同类型的新对象
class 类名{
          public:
                类名 (形参);//构造函数
                类名(const 类名 & 对象名);//复制构造函数
//...
};
类名::类(const 类名&对象名)//复制函数的实现
{ 函数体 } 
  • 复制构造函数被调用的三种情况
定义一个对象时,以本类另一个对象作为初始值,发生复制构造
如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造
这种情况也可以通过移动构造避免不必要的复制
  • 隐含的复制构造函数
如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个隐含的复制构造函数
这个构造函数执行的功能是:
用初始值对象的每个数据成员,初始化将要建立的对象的对应数据成员

6月14日 P59-P60实验

  • 构造函数
#include <iostream>
using namespace std;
//类定义
class Clock {
public:
	Clock(int newH, int newM, int newS);//构造函数
	void setTime(int newH, int newM, int newS);
	void showTime();
private://私有成员
	int hour, minute, second;
};
//构造函数的实现
Clock::Clock(int newH, int newM, int newS):
hour(newH), minute(newM), second(newS) {//初始化
}
void Clock::setTime(int newH, int newM, int newS) {
	hour = newH;
	minute = newM;
	second = newS;
}
inline void Clock::showTime() {
	cout << hour << ":" << minute << ":" << second << endl;
}
//主函数
int main()
{
	Clock c(0, 0, 0);//自动调用构造函数
	c.showTime();
	return 0;
}
  • 默认构造函数
#include <iostream>
using namespace std;
//类定义
class Clock {
public:
	Clock(int newH, int newM, int newS);//构造函数
	Clock();//默认构造函数
	void setTime(int newH, int newM, int newS);
	void showTime();
private://私有成员
	int hour, minute, second;
};
//默认构造函数的实现
Clock::Clock():hour(0),minute(0),second(0){}
//构造函数的实现
Clock::Clock(int newH, int newM, int newS):
hour(newH), minute(newM), second(newS) {//初始化
}
void Clock::setTime(int newH, int newM, int newS) {
	hour = newH;
	minute = newM;
	second = newS;
}
inline void Clock::showTime() {
	cout << hour << ":" << minute << ":" << second << endl;
}
//主函数
int main()
{
	Clock c1(8, 10, 0);//自动调用构造函数
	c1.showTime();
	Clock c2;//自动调用默认构造函数
	c2.showTime();
	return 0;
}

6月13日 P57-P58

  • 类和对象的程序举例
  • 类的定义
#include<iostream>
using namespace std;
class Clock{
     public:
      void setTime(ing newH = 0,int newM = 0,int newS = 0);//函数设置时间
      coid showTime();//函数显示时间
private:
      int hour,minute,second;//小时,分,秒
<pre>
*成员函数的实现
<pre>
void Clock::setTime(int newH,int newM,int newS){//实现成员函数
	hour = newH;
	minute = newM;
	second = newS;
}
void Clock::showTime(){
	cout <<hour<<":"<<minute<<":"<<second;
}
  • 对象的使用
int main(){
	Clock myClock;//定义实例
	myClock.setTime(8.30.30);//通信设置时间
	myClock.showTimne();//读取并显示时间
	return 0;
}
  • 构造函数
类中的特殊函数
用于描述初始化算法
  • 构造函数的作用
在对象被创建时使用特定的值构造对象
讲对象初始化为一个特定的初始状态
例如:
希望在构造一个Clock类对象时
将初始时间设为0:0:0
就可以通过构造函数来设置
  • 构造函数的形式
函数名与类名相同
不能定义返回值类型,也不能有return语句
可以有形式参数,也可以没有形式参数
可以是内联函数
可以重载
可以带默认参数值
  • 构造函数的调用时机
在对象创建时被自动调用
例如:
Clock myClock(0:0:0)
  • 默认构造函数
调用时可以不需要实参的构造函数
参数表为空的构造函数
全部参数都有默认值的构造函数
Clock();
Clock(int newH = 0,int newM = 0,int newS = 0)
这两个函数被调用时都可以不给参数
这两个函数不能同时出现在同一个类中
这两个函数不是合法的函数重载形式
  • 隐含生成的构造函数
如果程序中未定义构造函数,编译器将自动生成一个默认的构造函数
参数列表为空,不为数据成员设置初始值
如果类内定义了成员的初始值,则使用类定义的初始值
如果没有定义类内的初始值,则以默认方式初始化
基本类型的数据默认初始化的值是不确定的



6月12日 P56

  • 类和对象的定义
对象时现实中的对象在程序中的模拟
类时同一类对象的抽象,对象是类的实例
定义类的对象,才可以通过对象使用类中定义的功能
  • 定义类的语法形式
class类名称
{
  public:
        公有成员(外部接口)
  private:
        私有成员
  protected:
        保护型成员
};
  • 为数据成员设置类内初始值
  • 举例
class Clock{
public:
      void setTime(ing newH,int newM,int newS);
      coid showTime();
private:
      int hour = 0,minute = 0,second = 0;
};
  • 用于初始化数据成员
  • 类成员的访问控制
公有类型成员
私有类型成员
保护类型成员
  • 公有类型成员
在关键字public后面声明,它们是类与外部的接口
任何外部函数都可以访问公有类型数据和函数
  • 私有类型成员
在关键字private后面声明,只允许本类中的函数访问
而类外部的任何函数都不能访问
如果紧跟在类名称的后面声明私有成员,则关键字private可以省略
  • 保护类型成员
与private类似,其差别表现在继承与派生时对类的影响不同
  • 对象定义的语法
类名 对象名;
Clock myClock;
  • 类中成员之间可以直接使用成员名互相访问
  • 从类外访问成员使用“对象名,成员名”的方式访问public成员
  • 类的成员函数
在类中声明函数原型
可以在类外给出函数体实现,并在函数名前使用类名加以限定
也可以直接在类中给出函数体,并形成内联成员函数
允许声明重载函数和带默认参数值得函数
  • 内联成员函数
为了提高运行时的效率,对较简单的函数可以声明为内联形式
内联函数体中不要有复杂结构(循环语句和switch语句)
在类中声明内联成员函数的方式:
将函数体放在类的声明中
使用inline关键字


6月11日 P54-P55

  • 类与对象
对象:现实中对象的模拟,具有属性和行为
类:同一类对象的共同属性和行为
定义对象时:通过构造函数初始化
删除对象时:通过析构函数释放资源
  • 面向对象程序的基本特点
抽象、封装、继承和多态
  • 抽象
对同一类对象的共同属性和行为进行概括,形成类
首先注意问题的本质描述,其次是实现过程和细节
数据抽象:描述某类对象的属性或状态(对象相互区别的物理量)
int hour,int minute,int second;
代码抽象:描述某类对象的共有写行为特征或具有的功能
setTime(),showTime();
抽象的实现:类。
  • 封装
将抽象出的数据、代码封装在一起,形成类。
将抽象出的数据成员、代码成员相结合,将它们视为一个整体
目的:增强安全性和简化变成,使用者不必了解具体的实现细节
只需要通过外部接口,以特定的访问权限,来使用类的成员
实现封装:类声明中的{}。
  • 继承
在已有类的基础上,进行扩展形成新的类
  • 多态
多态:同一名称,不同功能实现方式
目的:达到行为标识统一,减少程序中标识符的个数

6月10日 P53——实验续

  • 函数重载
#include <iostream>
#include <cmath>
using namespace std;
//两个数的最大值
int max1(int x, int y);
//三个数的最大值
int max1(int x, int y, int z);
//两个双精度数的最大值
double max1(double x, double y);
//三个双精度数的最大值
double max1(double x, double y, double z);


//主函数
int main()
{
	//整数变量
	int a, b, c;
	//上精度浮点数变量
	double l, m, n;

	cout << "输入a:";
	cin >> a;

	cout << "输入b:";
	cin >> b;

	cout << "输入c:";
	cin >> c;
	cout << "\n";

	//求两个数最大值
	cout << "max of" << a << "and" << b << "is" << max1(a, b) << endl;

	//秋三个数最大值
	cout << "max of" << a << "," << b << "and" << c << "is" << max1(a, b, c) << endl;	
	cout << "\n\n";

	cout << "输入双精度数l:";
	cin >> l;

	cout << "输入双精度数m:";
	cin >> m;

	cout << "输入双精度数n:";
	cin >> n;
	cout << "\n";

	//求两个双精度数的最大值
	cout << "max of" << l << "and" << m << "is" << max1(l, m) << endl;

	//求三个双精度数的最大值
	cout << "max of" << l << "," << m << "and" << n << "is" << max1(l, m, n) << endl;

	return 0;
}
//两个数的最大值
int max1(int x, int y)
{
	if (x == y)
	{
		return x;
	}
	else if (x >=y)
	{
		return x;
	}
	else
	{
		return y;
	}
}
//三个数的最大值
int max1(int x, int y, int z)
{
	//复用了两个int型最大值的函数形式
	//先比较x,y,在用最大的和z作比较
	return max1(max1(x, y), z);
}
//两个双进度数的最大值
double max1(double x, double y)
{
	//判断两个双精度数是否相等
	//abs是系统库中的一个标准函数,是求某个数的绝对值
	if (abs(x-y)<1e-10)
	{
		return x;
	}
	else if (x >= y)
	{
		return x;
	}
	else
	{
		return y;
	}
}
//三个数最大值
double max1(double x, double y, double z)
{
	return max1(max1(x, y), z);
}
  • 计算次幂,利用系统函数功能完成
#include <iostream>
#include<cmath>
using namespace std;


int main()
{
	int x, y;

	cout << "x:";
	cin >> x;

	cout << "y:";
	cin >> y;

	cout << "\n";

	cout << "x=" << x << "\t";
	cout << "y=" << y << "\n";
	//pow 是系统库中的标准函数,是求x的y次幂
	cout << "pow(x and y)=" << pow(x, y);

	return 0;
}

6月9日 P51-P52——小结+实验

  • 应掌握的类容
函数的定义与调用、参数传递
内联函数、带默认数值的函数、函数重载
C++系统函数
  • 函数的应用举例
  • 函数声明在主函数之后
using namespace std;
//算法函数——华氏温度转换为摄氏温度
float Convert(float F)
{
	float  C;

	C = (F - 32) * 5 / 9;

	return C;
}
int main()
{
	float F;
	cout << "请输入华氏温度:\n";
	cin >> F;
	cout << "华氏温度转换的摄氏温度为:\n";
	cout << Convert(F);
	return 0;
}

  • 计算返回值
#include <iostream>
using namespace std;


int fib(int n);
int main()
{
	int n, answer;
	cout << "输入数字:";
	cin >> n;
	cout << "\n\n";
	answer = fib(n);//赋值给answer
	cout << answer << "是第" << n << "个Fibonacci数字\n";
	return 0;
}

int fib(int n)
{
	cout << "Processing fib(" << n << ")...";
	if (n < 3)
	{
		cout << "Return 1!\n";
		return(1);
	}
	else
	{
		cout << "Call fib(" << n - 2 << ")and fib(" << n - 1 << ".\n";
		return(fib(n - 2) + fib(n - 1));
	}
}

6月8日 P50

  • C++的系统函数
C++的系统库中提供了几百个函数可供程序员使用
例如:求平方根函数(sprt)和求绝对值函数(ads)
使用系统函数时要包含相应的头文件
例如:cmath
  • 系统函数举例应用
题目:
从键盘输入一个角度值,求出该角度的正弦值、余弦值和正切值
分析:
系统函数中提供了求正弦值、余弦值和正切值的函数
sin(),cos(),tan(),函数的声明在头文件cmath中
  • 示例代码
#include <iostream>
#include <cmath>
using namespace std;
const double PI = 3.14159265358979;
int main()
{
	//定义一个角度变量
	double angle;
	cout << "请输入一哥角度值: ";
	
	
	//输入角度值
	cin >> angle;


	//计算角度转换为弧度的公式
	double radian = angle * PI / 180;
	cout << "sin(" << angle << ")= " << sin(radian) << endl;
	cout << "cos(" << angle << ")= " << cos(radian) << endl;
	cout << "tan(" << angle << ")= " << tan(radian) << endl;
	return 0;
}



6月7日 P49

  • 函数重载
C++允许功能想进的函数在相同的作用域以相同函数声明
从而形成重载。//方便实用便于记忆
  • 形参相同和形参不同
1:int add(int x,int yy);
2:float add(float x,float y);
3:int add(int x,int y,int z);
在编译器中是合法的,只是返回值的类型不同
  • 注意事项
重载函数的形参必须不同:个数不同类型不同
编译程序将根据是参合形参的类型及个数的最佳匹配来选择调用哪一个函数
不要将功能不同的函数声明为重载函数,以免出现调用结果的误解、混淆。
  • 分别求两个证书的平方和和两个实数的平方和
#include <iostream>
using namespace std;

int sumOfsquare(int a, int b)
{
	return a * a + b * b;
}
double sumOfsquare(double  a, double b)
{
	return a * a + b * b;
}
int main()
{
	int m, n;
	cout << "输入两个数:";
	cin >> m >> n;
	cout << "这两个数的平方和是: " << sumOfsquare(n, m) << endl;
	double x, y;
	cout << "输入两个实数:";
	cin >> x >> y;
	cout << "这两个实数的平方和是:" << sumOfsquare(x, y) << endl;
	return 0;
}

6月6日 P48

  • 求长方体的体积
  • 函数getvolume计算体积
有三个形参:
length长
width宽
height高
其中width和height带有默认值2和3
  • 主函数中以不同形式调用getVolume函数
#include <iostream>
#include <iomanip>
using namespace std;

//声明原型
int getVolume(int length, int width = 2, int height = 3);

int main()
{
	const int x = 10, y = 12, z = 15;
	cout << "some box data is ";

	//第一次调用给出三个参数,按顺序分别赋值给x,y,z
	cout << getVolume(x, y, z) << endl;
	cout << "some box tata is ";

	//第二次调用给出两个参数,安顺序赋值x= 10,y  =12
	cout << getVolume(x, y) << endl;
	cout << "some box data is";
	
    //第三次调用给出一个参数,按顺序赋值x = 10
	cout << getVolume(x) << endl;
	return 0;
}
int getVolume(int length, int width, int height)
{
	cout << setw(5) << length << setw(5) << width << setw(5 << height << '\t');
		return length * width * height;
}

6月5日 P46-P47

  • constexpr函数
constexpr修饰的函数
在其所有参数都是constexpr时一定返回constexpr
  • constexpr函数举例
constexpr int get_size(){return 20;}
constexpr int foo = get_size();
foo是一个常量表达式
  • 带默认值的函数
可以预先设置默认的默认值,调用时如果给出实参,则采用实参
否则采用预先设置的默认参数值
int add(int x = 5,int y = 6)
{
   return x+y;
}
int main(){
   add(10,20);//计算10+20
   add(10);//计算10+6  只有一个实参时,优先赋值给前面的变量
   add();//计算5+6  
}
  • 默认参数值得说明次序
有默认参数的形参必须列在形参列表的最右
即默认参数值得右面不能有无默认值的参数;
调用时实参与形参的结合次序是从左到右。
int add(int x,int y = 5,int z = 6);//正确
int add(int x = 1,int y = 5,int z);//错误
int add(int x = 1,int y,int z = 6);//错误
  • 默认参数值与函数的调用位置
如果一个函数有原型声明,且原型声明在定义之前
则默认参数值应在函数原型声明中给出;
如果只有函数的定义,或函数定义在前
则默认参数值可以在函数定义中给出
  • 例1
int add(int x = 5,int y = 6);
//原型声明在前
int main(){
   add();
}
int add(int x,int y){
//此处不能再指定默认值
  return x = y;
}
  • 例2
int add(int x = 5,int y = 6){
//定义在调用之前
return x = y;
}
int main(){
   add();
}

6月4日 P45

  • 内联函数
内联函数声明时使用关键字inline
编译时在调用处用函数体进行替换
节省了参数传递
控制转移等开销
  • 注意
内联函数体内不能有循环语句和switch语句
内联函数的定义必须出现在内联函数第一次被调用之前
对内联函数不能进行异常接口声明
  • 应用举例
#include <iostream>
using namespace  std;
const double PI = 3.14159265358979;
//计算圆的面积
inline double calArea(double radius)//inline 建议编译器在调用函数的位置直接拿计算公式来替换
{
	return PI * radius * radius;
}
int main()
{
	double r = 3.0;
	double area = calArea(r);//计算公式直接嵌入到内部
	cout << "圆的面积是: " << area << endl;
	return 0;
}


6月3日 P44

  • 含有可变参数的函数
C++标准中提供了两种主要方法
如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型
如果实参的类型不同,我们可以编写可变参数的模板
  • initializer_list
initializer_list是一种标准库类型用于表示某种特定类型的值得数组
该类型定义在同名的头文件中
  • initializer_list的使用方法
initializer_list是一个类模板
使用模板时需要在模板名字后面跟一对尖括号“<>”,括号内给出类型参数
initializer_list<string> ls;//initializer_list的元素类型是string
initializer_list<int> li;//initializer_list的类型元素使int
initializer_list比较特殊的一点是,其对象中的元素永远是常量值
我们无法改变initializer_list对象中元素的值
含有initializer_list形参的函数也可以同时拥有其他形参
  • initializer_list的使用举例
在编写代码输出程序产生的错误信息时
最好用一个函数统一实现该功能
使得对错误的处理能够整齐划一
然而错误的信息种类不同
调用错误信息输出函数时传递的参数也会各不相同
使用initializer_list编写一个错误信息输出函数
使其可以作用于可变数量的形参

6月2日 P42-43

  • 函数的参数传递
在函数被调用时才分配形参的存储单元
实参可以是常量,变量或表达式
实参类型必须与形参相符
值传递是传递参数值,即单向传递
引用传递可以实现双向传递
常引用作参数可以保障实参数据的安全
  • 引用类型
引用(&)是标识符的别名
int &ri = i;//定义int引用ri,并初始化变量i的引用
定义一个引用时,必须同时对她进行初始化,是他只想一个已存在的对象
j = 10;
ri = j;//相当于i = j
一旦引用被初始化以后就不能改为指向其他对象
引用可以作为形参
  • 值传递
#include <iostream>
using namespace std;
//交换算法
void swap(int a, int b)
{
	int t = a;
	a = b;
	b = t;
}
int main()
{
	int x = 5, y = 10;
	cout << "x = " << x << "y = " << y << endl;
	swap(x, y);
	cout << "x = " << x << "y = " << y << endl;
	return 0;
}

6月1日 P40-P41

  • 递归算法
  • 从n个人中选择K个人组成一个委员会
#include <iostream>
using namespace std;

int comm(int n, int k)
{
	if (k > n)//n>k
	{
		return 0;//没有方案
	}
	else if (n == k || k == 0)//n=k或k=0
	{
		//递归终结点
		return 1;//只有1种方案
	}
	else
	{
		//n-1个人中选k个人+从n-1个人中选k-1个人
		return comm(n - 1, k) + comm(n - 1, k - 1);
	}
	
}
int main()
{
	int n, k;
	cout << "输入两个整数 n 和 k :";
	cin >> n >> k;
	cout << "C(n,k)=" << comm(n, k) << endl;
	return 0;
}
  • 递归函数的用法
#include <iostream>
using namespace std;
//把sre针的最上面一个盘子移动到dest针上
void move(char sre, char dest)
{
	cout << sre << " --> " << dest << endl;
}
//把n个盘子冲sre针移动到dest针,以medium针作为中介
void hanoi(int n, char sre, char medium, char dest)
{
	if (n ==1)
	{
		move(sre, dest);
	}
	else
	{
		hanoi(n - 1, sre, dest, medium);//将n-1个盘子移到中间柱子,以目标柱子为过度
		move(sre, dest);//最下面的盘子移到目标柱子
		hanoi(n - 1, medium, sre, dest);//将中间柱子上的n-1个盘子移动到目标柱子,以原柱子为过度
	}
}
int main()
{
	int  m;
	cout << "输入盘子数量: ";
	cin >> m;
	cout << "移动 " << m << " 个盘子的步骤:" << endl;
	hanoi(m, 'A', 'B', 'C');
	return 0;
}

5月31日 P38-P39

  • 函数的嵌套调用
  • 例:输入两个整数 求平方和
<source lang="cpp">
#include <iostream>
using namespace  std;


int fun1(int  m)//求平方
{
	return m * m;
}
int fun(int x, int y) //求和
{
	return fun1(x) + fun1(y);
}
int main()
{
	int a, b;
	cout << "请输入两个整数: ";
	cin >> a >> b;
	cout << "两个数的平方和为: " << fun(a, b) << endl;
	return 0;
}

</source>
*函数的递归调用
<pre>
函数自身调用自身,称为递归调用
#include <iostream>
using   namespace std;
//计算N的阶乘
unsigned fac(unsigned n)
{
	unsigned f;
	if (n == 0)//递归的终结条件
	{
		f = 1;
	}
	else
	{
		f = fac(n - 1) * n;//缩小规模,每次减小1
	}
	return f;
}
int main()
{
	unsigned n;
	cout << "输入一个整数: ";
	cin >> n;
	unsigned y = fac(n);
	cout << n << " 的阶乘 " << y << endl;
	return 0;
}

5月30日 P37

  • 例——骰子游戏
  • rand函数
函数原型:int rand(void)
所需头文件:<cstdlib>
功能和返回值:求出并返回一个伪随机数
  • srand函数
原型:void srand(unsigned int seed)
参数:seed产生随机数种子
所需头文件:<cstdlib>
功能:为使rand()产生一系列伪随机整数而设置始点
使用1作为seed参数,可以重置初始化rand()
  • 示例代码
#include <iostream>
#include <cstdlib>
using namespace std;
enum GameStatus { WIN, LOSE, PLAYING };//枚举类型游戏状态

int rollDice(void);
int main()
{
	int sum, myPoint;
	GameStatus status;//游戏状态
	unsigned seed;//没有赋值的随机种子

	cout << "请输入一个无符号整数:";
	cin >> seed;//输入的随机种子
	srand(seed);//将种子传递给rand()
	sum = rollDice();
	//第一轮投顾子、计算和数
	switch (sum)
	{
	case 7:
	{
		status = WIN;
		break;
	}
	case 11:
	{
		status = WIN;//如果和数为7和11则为胜利,状态为WIN
		break;
	}
	case 2:
	{
		status = LOSE;//和数为2、3、12则为输,状态为lose
		break;
	}
	case 3:
	{
		status = LOSE;
		break;
	}
	case 12:
	{
		status = LOSE;
		break;
	}
	default://其他情况,尚无结果 状态为playing 记录点数
	{
		status = PLAYING;
		myPoint = sum;
		cout << "投掷的点数为:" << myPoint << endl;
		break;
	}
	}
	while (status == PLAYING)//游戏状态为playing,继续游戏
	{
		sum = rollDice();
		if (sum == myPoint)//某一轮的和数等于点数则取胜
		{
			status = WIN;
		}
		else if (sum == 7)//出现和数为7则为失败
		{
			status = LOSE;
		}
	}
	//当游状态不为playing时,游戏结束输出游戏结果
	if (status == WIN)
	{
		cout << "游戏胜利" << endl;
	}
	else
	{
		cout << "游戏输了" << endl;
	}
	return 0;
}

int rollDice(void)
{
	int die1, die2, workSum;

	die1 = 1 + rand() % 6;
	die2 = 1 + rand() % 6;
	workSum = die1 + die2;
	cout << "Player rolled " << die1 << " + " << die2 << " = " << workSum << endl;

	return workSum;
}


5月29日 P35-P36

  • 求回文
#include <iostream>
using namespace std;
//是否回文
bool symm(unsigned n) {
	unsigned i = n;
	unsigned m = 0;
	while (i  >  0)
	{
		m = m * 10 + i % 10;
		i /= 10;
	}
	return m == n;
}
int main()
{
    for(unsigned m = 11; m < 1000; m++)
		if (symm(m) && symm(m*m) && symm(m*m*m))//把3次的与运算
		{
			cout << "m = " <<m;
			cout << " m * m =" << m*m;
			cout << " m * m * m = " << m*m*m << endl;
		}
	return 0;
}
  • 计算分段函数
#include <iostream>
#include <cmath>
using namespace std;
const double TINY_VALUE = 1e-10;
double tsin(double x) {
	double g = 0;
	double t = x;
	int n = 1;
	do
	{
		g += t;
		n++;
		t = -t * x * x / (2 * n - 1) / (2 / n - 2);
	} while (fabs(t) >= TINY_VALUE);//精度要求 控制循环条件
	return g;
}
int main()
{
	double k, r, s;
	cout << "r = ";
	cin >> r;
	cout << "s = ";
	cin >> s;
	if (r * r <= s * s )//根据大小比较结果  选择不同的公式
	{
		k = sqrt(tsin(r)*tsin(r) + tsin(s)*tsin(s));
	}
	else
	{
		k = tsin(r * s) / 2;
	}
	cout << k << endl;
	return 0;
}

5月28日 P33-P34

  • 数制转换
  • 二进制转换为十进制
#include <iostream>
using namespace std;
double power(double x, int n);//计算x的n次方
int main(){
	int  value = 0;
	cout << "输入一个8为字节数: ";
	for (int i = 7; i >= 0; i--)
	{
		char ch;
		cin >> ch;
		if (ch == '1')
			value += static_cast<int>(power(2, i));
	}
	cout << "十进制是 " << value << endl;
	return 0;
}
	double power(double x, int n ){
		double val = 1.0;
		while (n--)
			val *= x;
			return val;
}

  • π的计算公式
π=16arctan(1/5)-4arctan(1/239)
  • 设计一个函数计算arctan
double arctan(double x);
  • 主函数调用arctan
  • 代码示例 计算出π的值
#include <iostream>
using namespace std;


double arctan(double x) {
	double sqr = x * x;
	double e = x;
	double r = 0;
	int i = 1;
	while (e / i > 1e-15)//满足要求就‘+’一项,否则就不继续‘+’
	{
		double f = e / i;
		r = (i % 4 == 1) ? r + f : r - f;//i除以4的余数为1的时候应该‘+’一项
		e = e * sqr;                     //否则就‘-’一项
		i += 2;
	}
	return r;
}
int main()
{
	double a = 16.0 * arctan(1 / 5.0);
	double b = 4.0 * arctan(1 / 239.0);
	//PS:因为整数相除结果取整,如果参数写1/5,1/239,结果就是0
	cout << "PI = " << a - b << endl;
	return 0;
}

5月27日 P32

  • 函数调用
函数调用之前需要声明函数原型
函数原型:
类型标识符  被调用的函数名 (含有类型说明的形参表)
调用形式:
函数名(实参列表)
函数定义:
类型标识符函数名(形式参数表)
{
          语句序列
}
函数调用:
函数名(实参列表)
调用时用实参列表中的实参去初始化形参列表中的形参
嵌套调用:
在一个函数的函数体重,调用另一个函数
递归调用:
函数直接或间接的调用自身
  • 示例代码
#include <iostream>
using namespace std;
double power(double x, int n) 
{
	double val = 1.0;
	while (n--)
		val *= x;
	return val;//返回类型是double

}
int main()
{
	double pow;
	pow = power(5, 2);
	cout << "5  to the power 2 is :" << pow << endl;
	//函数调用作为表达式出现在输出语句中
	return 0;
}


5月26日 P29-P30

  • 函数
定义好的可用功能模块
定义函数:讲一个模块的算法用C++描述出来
函数名:功能模块的名字
函数的参数:计算所需要的数据和条件
函数的返回值:需要返回的计算结果
  • 函数定义的语法形式
类型标识符 函数名(形式参数表)
{
     语句序列
最后一句是return;
}
类型标识符可以定义函数
如果没有返回值:写void,不必写return语句


5月25日 P27-P28 复习课

  • do——while语句
#include <iostream>
using namespace std;
int main()
{
	int i = 1, sum = 00;
	do
	{
		sum += 1;
		i++;

	} while (i <= 10);
	cout << "sum = " << sum << endl;
	return 0;
}
  • fon循环语句
#include <iostream>
using namespace std;
int main()
{
	int i = 0, sum = 0;
	for (i = 1;i <= 10;i++)
	{
		sum += 1;
	}
	cout << "sum = " << sum << endl;
	return 0;
}
  • switch语句
 
#include <iostream>
using namespace std;
const float PI = 3.1416;
int main()
{
	int iType;
	float radius, a, b, area;
	cout << "图形的类型为?(1-圆形 2-长方形):";
	cin >> iType;
	switch (iType)
	{
	case 1:
		cout << "圆的半径为:";
		cin >> radius;
		area = PI * radius*radius;
		cout << "面积为:" << area << endl;
		break;
	case 2:
		cout << "矩形的长为:";
		cin >> a;
		cout << "举行的宽为:";
		cin >> b;
		area = a * b;
		cout << "面积为:" << area << endl;
		break;
	}
	return 0;
}

#include <iostream>
using namespace std;
struct MyTimeStruct //struct结构的作用十八一组相互关联的数据整合在一起
{
	unsigned int year;
	unsigned int month;
	unsigned int day;
	unsigned int hour;
	unsigned int min;
	unsigned int see;
};
int main()
{
	MyTimeStruct myTime = { 2020,5,26,21,0.0 };
	cout << "请输入年份:" << endl;
	cin >> myTime.year;
	cout << "请输入月份:" << endl;
	cin >> myTime.month;
	cout << "请输入日数:" << endl;
	cin >> myTime.day;
	cout << "请输入小时:" << endl;
	cin >> myTime.hour;
	cout << "请输入分钟:" << endl;
	cin >> myTime.min;
	cout << "请输入秒钟:" << endl;
	cin >> myTime.see;
	cout << "这个时间是:" << myTime.year << "/"
		<< myTime.month << "/"
		<< myTime.day << " "
		<< myTime.hour << ":"
		<< myTime.min << ":"
		<< myTime.see << endl;
	return 0;
}

5月24 P27

  • 类型别名
为已有类型另外命名
typedef 已有类型名 新类型名表
例:
typedef double Area,Volume;
typedef int Natural;
Natural i1,i2;
Area a;
Volume v;
  • using 新类型名=已有类型名
using Area = double
using Volume = double
  • 枚举类型
定义方式
将全部可取值——例举出来
语法形式
enum 枚举类型名 {变量值列表}
enum Weekday
 {SUN,MON,TUE,WED,THU,FRI,SAT}
  • C++包含两种枚举类型
不限定作用域枚举类型
enum 枚举类型名 {变量值列表}
限定作用域的enum类
  • 不限定作用域枚举类型
枚举元素是常量,不能对他们赋值
枚举元素具有默认值,他们依次为
0,1,2,3,4,5,6,7....
也可以在声明是另行制定枚举元素的值
例
enum Weekday{SUN = 7,MON = 1...}
枚举值可以进行关系运算
整数值不能直接赋给枚举变量
如果要将整数值赋值给枚举变量
应进行强制类型转换
枚举值可以赋给整型变量
</per>
*auto类型与decltype类型
<pre>
auto
编译器通过初始值自动推断变量的类型
例
auto val = val1 + val2
如果val1+val2是int类型,则val是int类型
如果val1+val2是double类型,则val是double类型
decltype
定义一个变量与某一表达式的类型相同
但并不用该表达式初始化变量
例
decltype(i)j = 2
表示j以2作为初始值,类型与i一致
  • 示例代码
#include <iostream>
using namespace std;
enum GameResult{WIN,LOSE,TIE,CANCEL};

int main()
{
	GameResult result;
	enum GameResult omit = CANCEL;
	for (int count = WIN;count <= CANCEL;count++)
	{
		result = GameResult(count);
		if (result == omit)
			cout << "the game was cancelled" << endl;
		else
		{
			cout <<  "the game was played";
			if (result == WIN)
				cout << "and we wonl";
			if (result == LOSE)
				cout << "and we lost";
			cout << endl;
		}
	}
	return 0;
}

5月23 P26

  • 循环结构与选择结构的嵌套
例2-10:
输入一系列整数,统计出正整数个数I和负整数个数J
读入0则结束
分析:
需要读入一系列整数,但是整数个数不定
要在每次读入之后进行判断
因此使用while循环最为合适
控制循环的条件是 n  !=  0
由于要判断数的正负并分别统计
所以需要在循环内嵌入选择结构
  • 其他控制语句
break语句
使程序从循环体和switch语句中跳出
继续执行逻辑上的下一条语句
不宜用在别处
continue语句
结束本次循环,接着判断是否执行下一次循环
goto语句
使程序的流程跳转到语句标号所指定的语句
不提倡使用
  • 示例代码
#include <iostream>
using namespace std;
int main()
{
	int i = 0, j = 0, n;
	cout << "输入一些数(0除外):" << endl;
	cin >> n;
	while (n != 0)
	{

		if (n > 0) i += 1; //i+1
		if (n < 0) j += 1;//j+1
		cin >> n;
	}
	cout << "正整数有:" << i << endl;
	cout << "负整数有:" << j << endl;
	return 0;
	
}

5月22日 P25

  • for(初始语句;表达式1;表达式2)语句
初始语句:循环先求解
表达式1:为true时执行循环体
表达式2:每次执行完循环体后求解
  • fon语句的另一种形式
  • 范围for循环
范围fon语句
for (声明:表达式)
语句
  • 示例代码,求一个数的因子
#include <iostream>
using namespace std;
int main()
{
	int  n;//定义变量
	cout << "输入一个数:\n";
	cin >> n;//输入的数存入n
	cout << "Number" << n << "factors \n";
	for (int k = 1;k <= n;k++)//定义初始化变量;控制条件;K自增1
	{
		if (n % k == 0)//那个数可以整除K
			cout << k << "\n";//输出K
	}
	cout << endl;
	return 0;
}



5月21日 P24

  • do——while语句
  • 语法形式
do语句:
可以是语句,其中必须含有改变条件表达式的语句
while(表达式):如果这个表达式为真
就继续循环
表达式为假,就跳出循环体
  • 执行顺序
先执行循环体语句,我判断条件
表达式为true时,继续执行循环体
  • 示例代码1
#include <iostream>
using namespace std;
int main()
{
	int n, right_digit, nernum = 0;//定义变量
	cout << "输入一个数:\n";//输入数据
	cin >> n;//接收数据存入 n
	cout << "这个数的翻转数是:\n";
	do  //无条件执行一遍
	{
		right_digit = n % 10;//不断的除以10取余
		cout << right_digit; //输出余数
		n /= 10; //n除以10 取整数商
	} while (n != 0);//判断n(商)是否等于10
	cout << endl;
	return 0;
}
  • 示例代码2
#include <iostream>
using namespace std;


int main()
{
	int i = 1, sum = 0;//定义变量i  和  sum
	do //无条件执行一次
	{
		sum += i;//等价于sum=sum+i 
		i++;//i+1  满足条件,累加1次  直到不满足条件
	}
	while (i <= 10);//while循环语句 执行条件
	cout << "sun的值是:" << sum << endl;//循环执行完毕,计算出i的值和sum的值
	return 0;
}

5月20日 P23

  • while训话语句
  • while语句的语法形式
while(表达式)  语句
语句:可以是复合语句
其中必须含有改变条件表达式值得语句
  • 执行顺序
先判断表达式的值,若为true时,执行语句
  • 代码示例
#include <iostream>
using namespace std;


int main()
{
	int i = 1, sum = 0;//定义变量i  和  sum
	while (i <= 10)//while循环语句 执行条件
	{
		sum += i;//等价于sum=sum+i 
		i++;//i+1  满足条件,累加1次  直到不满足条件
	}
	cout << "sun的值是:" << sum << endl;//循环执行完毕,输出i的值和sum的值
	return 0;
}

5月19日 P22

  • switch语句的语法
一般形式
switch(表达式)
{
    case  常量表达式1:语句1
    case  常量表达式2:语句2
            .
            .
            .
    case  常量表达式n:语句n
    default:          语句n+1
}
  • 执行顺序
以case中的常量表达式值为入口标号,由此开始顺序执行
因此,每个case分支最后应该加break语句
  • 注意
case分支可以包含多个语句,且不用{}。
表达式、判断值都是int型或char型。
如果若干分支执行内容相同可共用一组语句。
  • 代码
#include <iostream>
using namespace std;
int main()
{
	int day;//定义整数变量
	cout << "请输入1到6之间的任何一个整数:\n";
	cin >> day; //输入变量
	switch (day)//判断输入变量,进行匹配
	{
	case 0:
		cout << "Sunday" << endl; break;
	case 1:
		cout << "Monday" << endl; break;
	case 2:
		cout << "Tuesday" << endl; break;
	case 3:
		cout << "wednesday" << endl; break;
	case 4:
		cout << "Thursday" << endl; break;
	case 5:
		cout << "Friday" << endl; break;
	case 6:
		cout << "Saturday" << endl; break;
	default://如果变量超出范围  输出下一行提示
		cout << "Day out of range Sunday ... Saturday" << endl; break;
	}
	return 0;
}

5月18日 P21

  • If语句的语法形式
if(表达式)语句
例: if(x>y) cout << x;
if (表达式)语句1 else 语句2
例: if(x>y) cour  <<x;
else  cout<< y;
if(表达式1)语句1
else 
if(表达式2)语句2
else 
if(表达式3)语句3
...
else 语句 n
  • 嵌套的if结构
语法形式:每个if else 语句中都可以嵌套一个完整的if  else语句
注意:
语句1.2.3.4可以是复合语句
每层的if 与else 配对,或用{}来确定层次关系。
  • 测试代码
#include <iostream>
using namespace std;
int main()
{
	int x, y;
	cout << "Enter  x  and   y:";
	cin >> x >> y;
	if (x != y)
	
		if (x > y)
		cout << "x > y" << endl;
		else
		cout << "x<y" << endl;
	else
		cout << "x=y" << endl;
	
	return (0);


	}


5月17日 P20

  • 输入数据和输出数据
  • I/O流
在C++中,将数据从一个对象到另一个对象的流动抽象为“流”、
流在使用前要被建立,使用后要被删除
数据的输入与输出是通过I/O流来实现的,cin和cout是预定义的流类对象
cin用来处理标准输入,即键盘输入
cout用来处理标准输出,即屏幕输出
从流中获取数据的操作称为提取操作,向流中添加数据的操作称为插入操作
  • 预定义的插入符和提取符
“<<”是预定义的插入符,作用在六类对象cout上便可实现向标准输出设备输出
cout<<表达式<<表达...
标准输入是将提取符作用在流类对象cin上
cin>>表达式>>表达式
提取符可以连续写多个,每个后面跟一个表达式,该表达式通常是用于存放输入值的变量
例如:
int  a,b;
cin >> a >> b;
  • 常用的I/O流类库操作符
操作符名称                            含义       
  dec                        数值数据采用十进制表示
  hex                        数值数据采用十六进制表示
  oct                        数值数据采用八进制表示
  ws                         提取空白符
  endl                       插入换行符,并刷新流
  seds                       插入空字符
  setsprecision(int)       设置浮点数的小数位数(包括小数点)
  setw(int)                设置域宽
例:
cout<<setw(5)<<setsprecision(3)<<3.1415;

5月16日 实验课

  • 注释

注释掉的代码不会运行 也不会影响项目的大小

"/* , */":可以整段注释
"//":单行注释,只对本行代码进行注释,不影响前后的运行
  • 实验
#include <iostream>
#include <iostream>
using namespace std;
int main()
{
	cout << "The size of an int is:\t\t" << sizeof(int) << "bytes,\n";
	cout << "The size of an long is:\t\t" << sizeof(long) << "bytes,\n";
	cout << "The size of an bool is:\t\t" << sizeof(bool) << "bytes,\n";
	return 0;
}


5月15日 P18

  • 混合运算时数据类型转换—隐含转换
一些二元运算符
(算术运算符,关系运算符,逻辑运算符,位运算符和赋值运算符)
要求两个操作数的类型相同
在算数运算和关系运算中如果参与运算的操作类型不一致
编译系统会自动对数据进行转换(即隐含转换)
基本原则是
将低类型数据转换为高类型数据
  • 混合运算时数据类型的转换
 
将一个非布尔类型的算术值赋给布尔类型时
算术值为0则结果为false,否则结果为true
将一个布尔值赋给非布尔类型时
布尔值为false则结果为0
布尔值为true则结果为1
  • 混合运算时数据类型的转换
将一个浮点数赋给整数类型时
结果值将保留浮点数中的整数部分
小数部分将丢失
将整数值赋给浮点类型时
小数部分记为0
如果整数所占空间超过了浮点类型的容量
精度可能丢失
  • 混合运算时数据类型转换—显式转换
显式类型转换的作用
是将表达式的结果类型转换为
类型说明符所指定的类型
语法形式:
类型说明符(表达式)
(类型说明符)表达式
类型转换操作符<类型说明符>(表达式)
类型操作符可以是:
const_cast、dynameic_cast、reinerpret_cast、static_cast
例:
int(z),(int)z,static_cast<int>(z)三种完全等价

5月14日 P17续

  • 位运算 按位异或(^)
运算规则:两个操作数据进行异或
若对应位相同,则该结果为0
若对应位不同,则该结果为1
举例:计算071^052
         071:01111001
         052:00101010
     071^052:01010011
</per>
*是特定位翻转
<pre>
与0异或保持原值,与1异或取反
例:要使01111010低四位翻转
 01111010
 00001111
^01110101
  • 取反(~)
运算规则:单目运算符,对1个二进制数按位取反
例      025:0000000000010101
       ~025:1111111111101010
  • 移位(<<、>>)
左移运算<<
左移后 低位补0,高位舍弃
右移运算>>
右移后  低位舍弃  高位有符号就补符号位,高位无符号就补0

5月13日 P17

  • sizeof运算符
语法形式:sizeof(类型名)或sizeof(表达式)
结果值:“类型名”所指定的类型,或“表达式”的结果类型所占的字节数
例:sizeof(short)
sizeof x
  • 位运算—按位与(&)
运算规则:将两个运算量的每一位进行逻辑操作
举例:计算3&5
     3:00000011
     5:00000101
   3&5:000000001  
每一位进行对位,有一个是0结果就是0,都是1则结果是1
用途举例:将某一位置0,其他位不变
例如:将char型变量a的最低位置0:a = a & 0*fe
取出指定位:举例  有char C;int a;取出啊的低字节
置于C中:c = a & 0xfe
  • 按位或
运算规则:将两个运算量的每一位进行逻辑操作
计算3|5
     3:00000011
     5:00000101
   3|5:000000111
每一位进行对位,有一个是1结果就是1,都是1则结果是1,都是0则结果为0
用途举例:将某些位置1,其他位不变
例:将int型变量a的低字节置1
  a = a|0xfe

5月12日 P16

  • 逗号运算和逗号表达式
格式:表达式1,表达式2
求解顺序及结果:先求解表达式1,在求解表达式2.最终结果为表达式2的值
例:a=3*5,a*4  最终结果为60
  • 关系运算与关系表达式
关系运算是一种比较简单的一种逻辑运算,优先次序为:
< <= > >=(优先级相同(高)),== !=(优先级相同(低))
关系表达式是一种最简单的逻辑表达式:
其结果类型为bool,值只能为true或false
例:a>b,c<=a+b,x+y==3
  • 逻辑运算与逻辑表达式
逻辑运算符:!(非),&&(与),||(或) 优先级左边最高,往右依次降低
其结果类型为bool,值只能为true或false
逻辑表达式:(a<b)&&(x>y)
  • “&&”的运算规则
两侧表达式都为真,结果为真
有一侧表达式为假,结果为假
“&&(与)”的“短路特性” 表达式1&&表达式2
先求解表达式1
若表达式1的值为false,则最终结果为false,
不再求解表达式2
若表达式1的结果为true,则求解表达式2
以表达式2的结果作为最终结果
  • “||(或)”的运算规则
两侧表达式都为假,结果为假
有一侧表达式为真,结果为真
“||(或)”的短路特性 表达式1||表达式2
先求解表达式1
若表达式1的值为true,则最终结果为true
不再求解表达式2
若表达式1的值为false,则求解表达式2
以表达式2的结果作为最终结果
  • 条件运算符与条件表达式
一般形式:
表达式1?表达式2:表达式3
表达式1必须是bool类型
执行顺序:
先求解表达式1
若表达式1的值俄日true,则求解表达式2,
表达式21的值作为最终结果
若表达式1的职位false,则求解表达式3
表达式3的值作为最终结果
例:x=a>b?a:b
优先级:
条件运算符优先于赋值运算符,低于逻辑运算符
表达式1是bool类型,表达式2、3的类型可以不同
条件表达式的最终类型为2和3中较高的类型


5月11日 P15

  • 基本算术运算符
+,-,*,/ (若整整相除,结果取整)
%(取余,操作数为整数)
  • 优先级与结合性
先乘除,后加减,同级自左向右
  • ++,--(自增,自减)
例如:i++;--j
  • 赋值运算
赋值运算符“=”
将值赋给变量
  • 赋值表达式
用赋值运算符链接的表达式
例如:n=5
  • 复合的赋值运算符
有10中复合运算符:+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=
例如:a+3=a等价于a=a+3    x*=y+8等价于x=x*(y+8)

5月10日 P14续

  • 为常量命名,符号常量
#include <iostream>
using namespace std;

int main()
{
	const double pi(3.14159);//const常量  定义一个符号常量
	int radius;//定义变量  int表示变量是整数类型
	cout << "请输入radius值!\n";
	cin >> radius;//输入一个数存入radius变量中
	cout << "第一个radius值是:" << radius << '\n';//输出变量radius的值,及说明信息
	cout << "PI is:" << pi << '\n';//输出常量PI  及说明信息
	cout << "请再次输入一个不同radius值!\n";
	cin >> radius;//输入一个不同的radius值也存入radius中
	cout << "第二个radius值是:" << radius << '\n';//输出变量radius的第二个值
	cout << "PI is still:" << pi << '\n';//再次输入常量,这个只是不变的
	return 0;

}
  • 在程序中直接为变量赋值
#include <iostream>
using namespace std;

int main()
{
	const double pi(3.14159);//const常量  定义一个符号常量
	int radius(0);//定义变量 为变量赋值  int表示变量是整数类型
	cout << "radius值是:" << radius << '\n';//输出变量radius的值,及说明信息
	cout << "PI is:" << pi << '\n';//输出常量PI  及说明信息
	return 0;

}

5月9日 P14

  • 读入输出并显示整数
#include <iostream>
using namespace std;

int main()
{
	int radius;//定义变量 radius 没有初始化
	cout << "Please enter the radius!\n";//输出提示信息
	cin >> radius;//从键盘读入值放到 radius中,由cin这个对象的提取操作符
	cout << "The radius is:" << radius << '\n';//输出提示信息。把radius变量自己放到插入运算符后面
	cout << "PI is:" << 3.14 << '\n';//输出提示信息以及3.14这个常量
	cout << "Please enter a different radius!\n";//输出提示信息,输入不同radius变量值
	cin >> radius;//体现radius是一个变量,可以再次从键盘读入一个不同的值放到radius里面
	cout << "Now the dadius is changed to:"//输出radius
		<< radius << '\n';//进行验证
	return 0;
   
}


5月8日 P13

  • C++能够处理的基本数据类型
整数类型
实数类型
字符类型
布尔类型
  • 程序中的数据
  • 常量
在源程序中直接写明的数据
其值在整个程序运行期间不可改变
  • 变量
在程序运行过程中允许改变的数据
  • 整数类型
基本的整数类型:int
按符号分:符号的(signed),无符号的(unsigned)
  • 按数据范围分
短整数(short)
长整数(long)
长长整数(long long)
  • ISO C++标准并没有明确规定每种数据类型的字节数和取值范围

它只是规定它们之间的字节数大小顺序满足

(signed/unsigned)signed char
≤(unsigned)short int
≤(unsigned)int
≤(unsigned)long int
≤long long int
  • 字符类型(char)
容纳单个字符的编码
实质上存储也是整数
  • 浮点数类型
单精度(float)
双精度(double)
扩展精度(long double)
  • 字符串类型
有字符串常量
基本类型中没有字符串变量
采用字符数组存储字符串 (C风格的字符串)
标准C++类库中的String类  (C风格的字符串)
  • 布尔类型(bool)
只有两个值 true(真),false(假)
常用来表示关系比较、相等比较或逻辑运算的结果
  • 常量
在程序运行的整个过程中其值是种不可改变的量
直接使用符号(文字)表示的值
例如:12、35、A 都是常量
  • 整数常量
以文字形式出现的整数
十进制:  若干个0-9的数值,但数字部分不能以0开头,正数前面的正号可以省略
八进制:   前导0+若干个0-7的数字
十六进制: 前导0+若干个0-9的数字以及A-F的字母(大小写均可)
  • 后缀
L或l       表示类型至少是long
LL或ll     表示类型是long  long
U或u       表示unsigned类型
  • 浮点类型常量
  • 以文字形式出现
一般形式:例如 15.5,-15.2等
指数形式:例如 0.345E+2,-34.5E-3 整数部分和小数部分可以省略其一
浮点常量默认为double型,如果后缀为E或f可以使其成为float型 12.3f
  • C风格字符串常量
一对双引号括起来的字符序列
在内存中按串中字符的排列次序顺序存放,每个字符占一个字节
在末尾添加‘\0’作为结尾标记
通过添加前缀可以改变字符常量或者字符串常量的类型
  • 变量定义
数据类型:变量名1.....变量名n
在定义变量的同时也可以对它初始化
初始化方式:
int a=0
int a(0)
int a= {0}使用大括号的方式是列表初始化,不允许信息丢失
int a{0}
  • 符号常量
常量定义语句的形式为:const 数据类型说明符 常量名=变量值
const float  PI=3.1415926
符号常量在定义是一定要初始化,在场层序中间不能改变其值

5月7日 P11-P12

  • C++字符集
大小写英文字母
数字字符
特殊字符
  • C++构词法
  • 关键字
C++预定义的单词
  • 标识符
程序员声明的单词,它命名程序正文中的一些实体
  • 文字
在程序中直接用符号表示的数据
  • 分隔符
(){},:;用于分隔各个词法记号或程序正文
  • 运算符(操作符)
用于实现各种运算的符号
  • 空白符
空格符、制表符(TAB键产生的字符)、垂直制表符、换行符、回车符和注解的总称
  • 标识符的构成规则
以大写字母、小写字母或下划线(_)开始
可以由大写字母、小写字母、下划线(_)或数字0~9组成
大写字母和小写字母代表不同的标识符
不能是C++关键字或操作符

5月6日 P10 第二章

  • C++能够处理的基本数据类型
整数类型
实数类型
字符类型
布尔类型
  • C++支持的基本运算
算数运算
逻辑运算
  • 程序要能够输入、输出数据
  • C++中的数据输入、输出可以调用预定义的功能模块实现
  • 程序的执行流程不总是顺序的,因此程序要能够
对执行流程进行选择(选择、开关语句)
反复用同一算法依次处理大批量数据(循环语句)
  • 枚举类型
通过列出所有可取值来定义一种新类型

5月4日和5月5日 P9实验课

下载安装初步了解和使用VS2017 完成初步编程

#include <iostream>
using namespace std;

int main()
{
	cout << "Hello\n";
}

运行并生成EXE文件成功

5月3日 P8 第一章理论——完

二进制数的编码如何表示

源码“符号—绝对值”表示的编码
源码的缺点:零的表示不唯一  进行四则运算时符号位必须单独处理且运算规则复杂
补码:0的表示是唯一的  符号位可以作为数值参加运算 减法运算可以转换为加法运算
模数:n位二进制整数的模数位2" n位小数的模数位2
补数:一个数减去另一个数(加一个负数)等于第一个数加第二个数的补数

补码的计算规则:

1.负整数:源码的符号位不变(1仍是1),其余各位取反(0变1,1变0)
反码:作为中间码 负数补码-反码+1  正数补码=源码
补码的优点:0表示唯一   符号位可以作为数值参加运算  补码运算的结果仍为补码
补码再次求补即可得到源码
如果负数之和得正数或正数之和得负数,说明运算结果溢出
实数的浮点表示:通常用浮点方式表示小数
字符数据的表示:字符通过编码表示   ASCII码:常用的西文字符编码  汉字编码:中国国家标准

5月2日 P5-P7

  • 源程序:用源语言写的程序,需要翻译的程序
  • 目标程序:源程序经过加工以后生成的机器语言程序
  • 可执行程序:连接目标程序以及库中的某些文件生成的一个可执行文件 (EXE文件)
  • 汇编程序:将汇编语言源程序翻译成目标程序
  • 编译程序:将高级语言源程序翻译成目标程序
  • 解释程序:将高级语言源程序翻译成机器指令,边翻译边执行 (Java语言,半编译半执行)
  • C++程序是直接编译为本地机器语言代码
C++程序的开发过程:
1,算法与数据结构设计
2,源程序编辑
3,编译
4,链接
5,测试
6,调试
  • 计算机中的信息与存储单位
  • 功能:算数运算和逻辑运算

计算机的信息:

控制信息  指挥计算机操作
数据信息  计算机程序加工的对象
数据信息:数值信息和非数值信息
数值信息:分为定点数和浮点数
非数值信息:分为字符数据和逻辑数据
信息存储单位: 位(bit,b) 数据的最小单位,表示一位二进制信息
字节(byte,B)  8位二进制数字组成(1byte=8bit)
千字节 1KB=1024B
兆字节 1MB=1024K
吉字节 1GB=1024M
都是按2的多少次方来计算

计算机的数字系统 二进制系统和基本符号(0,1)

二进制   基数是2  进位原则是逢2进1   基本符号是  0,1
八进制   基数是8  进位原则是逢8进1   基本符号是 0,1,2,3,4,5,6,7
十进制   基数是10 进位原则是逢8进1   基本符号是 0-9
十六进制 基数是16 进位原则是逢16进1  基本符号是 0-9,A,B,C,D,E,F 

R进制转成十进制:各位数字与它的权相乘,其积相加
权重
权
十进制整数转成R进制整数:{除以R取余法}低位到高位
十进制小数转成R进制小数:{乘以R取整法}高位到低位


5月1日 观看P1-P4

  • 面向对象思维
  • 对象:现实中的一个个体
  • 程序中,用相应的机制可以模拟出对象
  • 类:基于分类和抽象的思维形成的,同一类对象可以抽象出其共同行为和属性形成类
  • 类与对象的关系:类型和实例之间的关系
  • 封装:隐藏内部的属性,对外部有边界,只保留有限的对外接口,使用方便、安全性好
  • 继承:用于对象的重复使用,改造已有的类,形成新的类
  • 多态:同样的信息作用在不同的对象上,会引起不同的行为